├── .github └── dependabot.yml ├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── plexpt │ └── chatgptonlinejava │ ├── ChatgptOnlineJavaApplication.java │ ├── app │ ├── BalanceDTO.java │ ├── ChatController.java │ ├── ChatParam.java │ ├── GPTEventSourceListener.java │ ├── GlobalExceptionHandler.java │ ├── KeyConfig.java │ ├── KeyManager.java │ ├── Result.java │ ├── SubscriptionData.java │ └── UseageResponse.java │ └── util │ ├── CircularBlockingQueue.java │ ├── CircularQueue.java │ └── SseHelper.java └── resources ├── application.yml └── static ├── boot.html ├── css ├── common.css ├── iconfont.ttf ├── iconfont.woff ├── iconfont.woff2 └── wenda.css ├── faq1.html ├── faq2.html ├── favicon.ico ├── index.html └── sse.min.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: maven 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 100 8 | 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ChatGPT Java Online Demo 2 | 3 | 4 | 项目基于 Java 8、Spring Boot 和 Maven。 5 | 6 | ![image](https://github.com/user-attachments/assets/6c3cb9f8-8488-41c9-b1ad-acc1f40593ac) 7 | 8 | 9 | ## 1. 本地运行 10 | 11 | 1. 使用 IntelliJ IDEA 打开项目。 12 | 2. 修改 `src/main/resources/application.yml` 文件中的 `KEY LIST`。每行一个 Key,自动轮询,至少放入 2 个。如果没有不同的 Key,可以重复使用相同的 Key。 13 | 3. 运行 `ChatgptOnlineJavaApplication` 类以启动项目。 14 | 15 | ## 2. 服务器部署 16 | 17 | ### 打包 18 | 19 | 在项目根目录下执行以下命令打包项目: 20 | 21 | ```shell 22 | mvn package 23 | ``` 24 | 25 | 打包完成后,将生成的 jar 文件上传到服务器,并确保服务器上已安装 Java 8。然后通过以下命令运行: 26 | 27 | ```shell 28 | java -jar chatgptonlinejava.jar 29 | ``` 30 | 31 | ### 注意事项 32 | 33 | 由于国内服务器无法直接访问openai,请使用代理或部署在国外服务器。代理设置可在 `ChatController` 类中进行配置。 34 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.7.8 9 | 10 | 11 | com.plexpt 12 | chatgpt-online-java 13 | 2.0 14 | chatgpt-online-java 15 | chatgpt-online-java 16 | 17 | 1.8 18 | 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-web 23 | 24 | 25 | 26 | 27 | org.projectlombok 28 | lombok 29 | true 30 | 31 | 32 | 33 | net.dreamlu 34 | mica-http 35 | 2.7.9 36 | 37 | 38 | org.apache.commons 39 | commons-lang3 40 | 41 | 42 | com.alibaba 43 | fastjson 44 | 2.0.52 45 | 46 | 47 | 48 | com.github.plexpt 49 | chatgpt 50 | 5.0.1 51 | 52 | 53 | org.springframework.boot 54 | spring-boot-starter-test 55 | test 56 | 57 | 58 | 59 | 60 | chatgptonlinejava 61 | 62 | 63 | org.springframework.boot 64 | spring-boot-maven-plugin 65 | 66 | 67 | 68 | org.projectlombok 69 | lombok 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/ChatgptOnlineJavaApplication.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.boot.ApplicationArguments; 5 | import org.springframework.boot.ApplicationRunner; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | 9 | @SpringBootApplication 10 | public class ChatgptOnlineJavaApplication implements ApplicationRunner { 11 | 12 | @Value("${server.port}") 13 | Integer port; 14 | 15 | public static void main(String[] args) { 16 | 17 | SpringApplication.run(ChatgptOnlineJavaApplication.class, args); 18 | } 19 | 20 | @Override 21 | public void run(ApplicationArguments args) throws Exception { 22 | 23 | System.out.println("=================================================="); 24 | System.out.println("启动成功"); 25 | System.out.println("本地访问地址:http://localhost:" + port); 26 | System.out.println("访问地址:http://你的服务器IP:" + port); 27 | 28 | System.out.println("=================================================="); 29 | 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/BalanceDTO.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class BalanceDTO { 7 | 8 | 9 | double total_available; //剩余 10 | 11 | String total_used; //已使用 12 | 13 | 14 | String total_granted;//全部 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/ChatController.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | 4 | import com.plexpt.chatgpt.ChatGPT; 5 | import com.plexpt.chatgpt.ChatGPTStream; 6 | import com.plexpt.chatgpt.entity.chat.ChatCompletion; 7 | import com.plexpt.chatgpt.entity.chat.ChatCompletionResponse; 8 | import com.plexpt.chatgpt.entity.chat.Message; 9 | import lombok.RequiredArgsConstructor; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.web.bind.annotation.*; 12 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 13 | 14 | import java.net.Proxy; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | @Slf4j 20 | @RestController 21 | @RequestMapping 22 | @RequiredArgsConstructor 23 | public class ChatController { 24 | 25 | final KeyManager keyManager; 26 | 27 | //代理可以为null 28 | //端口是你的魔法端口 29 | // static Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 1081)); 30 | Proxy proxy = Proxy.NO_PROXY; 31 | 32 | //实际请用数据库管理上下文 33 | private static Map> context = new HashMap<>(); 34 | 35 | 36 | @PostMapping("chat") 37 | public ChatCompletionResponse chat(@RequestBody ChatParam param) { 38 | 39 | log.info("正在提问: " + param.getMessage()); 40 | ChatCompletionResponse completion = getText(param); 41 | String text = completion.getChoices().get(0).getMessage().getContent(); 42 | 43 | log.info("问题:" + param.getMessage() + "\n回答:" + text); 44 | 45 | return completion; 46 | } 47 | 48 | @GetMapping("/chat/sse") 49 | @CrossOrigin 50 | public SseEmitter sseEmitter(@RequestBody ChatParam param) { 51 | 52 | ChatGPTStream chatGPTStream = ChatGPTStream.builder() 53 | .timeout(50) 54 | .apiKey(param.getApiKey()) 55 | .proxy(proxy) 56 | .apiHost(param.getApiHost()) 57 | .build() 58 | .init(); 59 | 60 | SseEmitter sseEmitter = new SseEmitter(-1L); 61 | 62 | GPTEventSourceListener listener = new GPTEventSourceListener(sseEmitter); 63 | 64 | ChatCompletion chatCompletion = ChatCompletion.builder() 65 | .messages(param.getMessages()) 66 | .model(param.model) 67 | .maxTokens(param.getMax_tokens()) 68 | .stream(true) 69 | .build(); 70 | 71 | chatGPTStream.streamChatCompletion(chatCompletion, listener); 72 | 73 | return sseEmitter; 74 | } 75 | 76 | 77 | private ChatCompletionResponse getText(ChatParam param) { 78 | 79 | 80 | ChatGPT chatGPT = ChatGPT.builder() 81 | .apiKey(param.getApiKey()) 82 | .timeout(50) 83 | .proxy(proxy) 84 | .apiHost(param.getApiHost()) 85 | .build() 86 | .init(); 87 | 88 | try { 89 | ChatCompletion chatCompletion = ChatCompletion.builder() 90 | .messages(param.getMessages()) 91 | .model(param.model) 92 | .maxTokens(param.getMax_tokens()) 93 | .stream(false) 94 | .build(); 95 | 96 | ChatCompletionResponse completion = chatGPT.chatCompletion(chatCompletion); 97 | 98 | return completion; 99 | 100 | } catch (Exception e) { 101 | log.error("API调用出错:{}", e); 102 | throw new RuntimeException("请检查KEY, 网络。请输入你的APIKEY后试用: " + e.getMessage()); 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/ChatParam.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import com.plexpt.chatgpt.entity.chat.Message; 4 | import lombok.Data; 5 | import lombok.NonNull; 6 | import org.springframework.util.StringUtils; 7 | 8 | import java.util.List; 9 | 10 | @Data 11 | public class ChatParam { 12 | 13 | 14 | private String apiKey; 15 | 16 | String model; 17 | private @NonNull List messages; 18 | 19 | Integer max_tokens; 20 | Double temperature; 21 | Boolean stream; 22 | String message; 23 | List> context; 24 | String key; 25 | String id; 26 | private String apiHost; 27 | 28 | public boolean hasKey() { 29 | return !StringUtils.isEmpty(key); 30 | } 31 | 32 | 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/GPTEventSourceListener.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.plexpt.chatgpt.entity.chat.ChatCompletionResponse; 5 | import com.plexpt.chatgpt.entity.chat.Message; 6 | import com.plexpt.chatgptonlinejava.util.SseHelper; 7 | 8 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 9 | 10 | import java.util.Objects; 11 | import java.util.function.Consumer; 12 | 13 | import lombok.RequiredArgsConstructor; 14 | import lombok.Setter; 15 | import lombok.SneakyThrows; 16 | import lombok.extern.slf4j.Slf4j; 17 | import okhttp3.Response; 18 | import okhttp3.ResponseBody; 19 | import okhttp3.sse.EventSource; 20 | import okhttp3.sse.EventSourceListener; 21 | 22 | /** 23 | * 描述:OpenAIEventSourceListener 24 | * 25 | * @author https:www.unfbx.com 26 | * @date 2023-02-22 27 | */ 28 | @Slf4j 29 | @RequiredArgsConstructor 30 | public class GPTEventSourceListener extends EventSourceListener { 31 | 32 | final SseEmitter sseEmitter; 33 | 34 | String last = ""; 35 | @Setter 36 | Consumer onComplate = s -> { 37 | 38 | }; 39 | 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public void onOpen(EventSource eventSource, Response response) { 46 | 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @SneakyThrows 53 | @Override 54 | public void onEvent(EventSource eventSource, String id, String type, String data) { 55 | sseEmitter.send(data); 56 | 57 | // log.info("回答中:{}", data); 58 | // if (data.equals("[DONE]")) { 59 | // log.info("回答完成:" + last); 60 | // onComplate.accept(last); 61 | // SseHelper.complete(sseEmitter); 62 | // return; 63 | // } 64 | // 65 | // ChatCompletionResponse completionResponse = JSON.parseObject(data, 66 | // ChatCompletionResponse.class); // 读取Json 67 | // Message delta = completionResponse.getChoices().get(0).getDelta(); 68 | // String text = delta.getContent(); 69 | // if (text != null) { 70 | // last += text; 71 | // 72 | // sseEmitter.send(delta); 73 | // } 74 | } 75 | 76 | 77 | @Override 78 | public void onClosed(EventSource eventSource) { 79 | SseHelper.complete(sseEmitter); 80 | } 81 | 82 | 83 | @SneakyThrows 84 | @Override 85 | public void onFailure(EventSource eventSource, Throwable t, Response response) { 86 | if (Objects.isNull(response)) { 87 | return; 88 | } 89 | ResponseBody body = response.body(); 90 | if (Objects.nonNull(body)) { 91 | log.error("OpenAI sse连接异常data:{},异常:{}", body.string(), t); 92 | } else { 93 | log.error("OpenAI sse连接异常data:{},异常:{}", response, t); 94 | } 95 | eventSource.cancel(); 96 | SseHelper.complete(sseEmitter); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/GlobalExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import org.springframework.web.bind.annotation.ExceptionHandler; 4 | import org.springframework.web.bind.annotation.RestControllerAdvice; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | 8 | import lombok.extern.slf4j.Slf4j; 9 | 10 | /** 11 | * @author plexpt 12 | */ 13 | @Slf4j 14 | @RestControllerAdvice 15 | public class GlobalExceptionHandler { 16 | 17 | private static final String ERROR_MSG = "服务器挤爆了,请充值或稍后尝试"; 18 | 19 | @ExceptionHandler(RuntimeException.class) 20 | public Result handleBaseException(Exception ex, HttpServletRequest request) { 21 | log.warn("Handle exception, message={}, requestUrl={}", ex.getMessage(), 22 | request.getRequestURI()); 23 | 24 | return Result.error(ex.getMessage()); 25 | } 26 | 27 | @ExceptionHandler(Exception.class) 28 | public Result handleDefaultErrorView(Exception ex, HttpServletRequest request) { 29 | log.error("Handle exception, message={}, requestUrl={}", ex.getMessage(), 30 | request.getRequestURI(), ex); 31 | 32 | return Result.error(ex.getMessage()); 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/KeyConfig.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | import lombok.Data; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | 13 | @Slf4j 14 | @Data 15 | @Configuration 16 | @ConfigurationProperties(prefix = "keys") 17 | public class KeyConfig { 18 | 19 | 20 | List list = new ArrayList<>(); 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/KeyManager.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import com.plexpt.chatgptonlinejava.util.CircularBlockingQueue; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.ApplicationArguments; 7 | import org.springframework.boot.ApplicationRunner; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.List; 11 | 12 | import lombok.extern.slf4j.Slf4j; 13 | 14 | 15 | @Slf4j 16 | @Service 17 | public class KeyManager implements ApplicationRunner { 18 | 19 | private static CircularBlockingQueue keyQueue = new CircularBlockingQueue<>(); 20 | 21 | @Autowired 22 | KeyConfig config; 23 | 24 | public synchronized String getKey() { 25 | 26 | String next = keyQueue.next(); 27 | return next; 28 | } 29 | 30 | 31 | @Override 32 | public void run(ApplicationArguments args) throws Exception { 33 | try { 34 | log.info("开始配置KEY队列"); 35 | 36 | List list = config.getList(); 37 | int size = list.size(); 38 | 39 | log.info("找到" + size + "个配置的KEY"); 40 | 41 | for (String key : list) { 42 | keyQueue.add(key); 43 | } 44 | 45 | } catch (Exception e) { 46 | e.printStackTrace(); 47 | } 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/Result.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | 4 | import lombok.Data; 5 | 6 | @Data 7 | public class Result { 8 | 9 | String status = "success"; 10 | 11 | String msg; 12 | String message; 13 | String raw_message; 14 | 15 | public static Result ok(String text) { 16 | Result result = new Result(); 17 | result.setRaw_message(text); 18 | result.buildMsg(); 19 | 20 | return result; 21 | } 22 | 23 | public static Result error(String message) { 24 | Result result = new Result(); 25 | result.setMessage(message); 26 | result.setRaw_message(message); 27 | return result; 28 | 29 | } 30 | 31 | public void buildMsg() { 32 | message = "

" + raw_message + "

\n"; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/SubscriptionData.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | import java.math.BigDecimal; 7 | 8 | import lombok.Data; 9 | 10 | @Data 11 | public class SubscriptionData { 12 | 13 | /** 14 | * 金额:美元 15 | */ 16 | @JsonProperty("hard_limit_usd") 17 | private BigDecimal hard_limit_usd; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/app/UseageResponse.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.app; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | 5 | import java.math.BigDecimal; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * 余额查询接口返回值 11 | * 12 | * @author plexpt 13 | */ 14 | @Data 15 | public class UseageResponse { 16 | 17 | /** 18 | * 总使用金额:美元 19 | */ 20 | @JsonProperty("total_usage") 21 | private BigDecimal total_usage; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/util/CircularBlockingQueue.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.util; 2 | 3 | import java.util.concurrent.locks.ReentrantLock; 4 | 5 | /** 6 | * Created by giant039 on 2017/3/17. 7 | * 阻塞环形队列 高并发 8 | * 9 | * @param 10 | */ 11 | public class CircularBlockingQueue extends CircularQueue { 12 | /** 13 | * 对添加,删除,指针移动操作加锁 14 | */ 15 | protected final ReentrantLock putLock = new ReentrantLock(); 16 | 17 | private QueueListener listener; 18 | 19 | public CircularBlockingQueue() { 20 | super(); 21 | } 22 | 23 | public CircularBlockingQueue(QueueListener listener) { 24 | super(); 25 | this.listener = listener; 26 | } 27 | 28 | public void setListener(QueueListener listener) { 29 | this.listener = listener; 30 | } 31 | 32 | @Override 33 | public boolean add(E e) { 34 | final ReentrantLock putLock = this.putLock; 35 | try { 36 | putLock.lockInterruptibly(); 37 | super.add(e); 38 | 39 | if (listener != null) { 40 | listener.afterAdd(e); 41 | } 42 | 43 | return true; 44 | } catch (InterruptedException exp) { 45 | exp.printStackTrace(); 46 | return false; 47 | } finally { 48 | putLock.unlock(); 49 | } 50 | 51 | } 52 | 53 | @Override 54 | public E next() { 55 | final ReentrantLock putLock = this.putLock; 56 | try { 57 | putLock.lockInterruptibly(); 58 | return super.next(); 59 | } catch (InterruptedException e) { 60 | e.printStackTrace(); 61 | return null; 62 | } finally { 63 | putLock.unlock(); 64 | } 65 | 66 | } 67 | 68 | @Override 69 | public E prev() { 70 | final ReentrantLock putLock = this.putLock; 71 | try { 72 | putLock.lockInterruptibly(); 73 | return super.prev(); 74 | } catch (InterruptedException e) { 75 | e.printStackTrace(); 76 | return null; 77 | } finally { 78 | putLock.unlock(); 79 | } 80 | } 81 | 82 | @Override 83 | public boolean remove(E e) { 84 | final ReentrantLock putLock = this.putLock; 85 | try { 86 | putLock.lockInterruptibly(); 87 | 88 | if (listener != null) { 89 | listener.afterAdd(e); 90 | } 91 | 92 | return super.remove(e); 93 | } catch (InterruptedException exp) { 94 | exp.printStackTrace(); 95 | return false; 96 | } finally { 97 | putLock.unlock(); 98 | } 99 | } 100 | 101 | 102 | /** 103 | * 监听器监听插入,删除,等操作之后需要实现的功能 104 | */ 105 | interface QueueListener { 106 | void afterAdd(E e); 107 | 108 | void afterRemove(E e); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/util/CircularQueue.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.util; 2 | 3 | /** 4 | * 环形队列 5 | * 6 | * @param 7 | */ 8 | public class CircularQueue { 9 | private int size; 10 | 11 | //指针 12 | private Node node; 13 | 14 | private Node first; 15 | private Node last; 16 | 17 | private final int MODE_NEXT = 0; 18 | private final int MODE_PREV = 1; 19 | private int lastMode = MODE_NEXT; //最后一次操作,0为next,1为prev 20 | 21 | 22 | public CircularQueue() { 23 | 24 | } 25 | 26 | /** 27 | * 加入队列 28 | * 29 | * @param e 30 | */ 31 | public boolean add(E e) { 32 | final Node l = last; 33 | final Node newNode = new Node<>(l, e, first); 34 | last = newNode; 35 | 36 | if (node == null) { 37 | node = newNode; //指针 38 | } 39 | if (l == null) { 40 | first = newNode; 41 | first.prev = first; 42 | } else { 43 | l.next = newNode; 44 | first.prev = l.next; 45 | } 46 | 47 | size++; 48 | return true; 49 | } 50 | 51 | /** 52 | * 返回当前指针元素并把指针指向下一个元素 53 | * 54 | * @return 55 | */ 56 | public E next() { 57 | if (node == null) { 58 | return null; 59 | } 60 | E e = node.item; 61 | node = node.next; 62 | 63 | lastMode = MODE_NEXT; 64 | return e; 65 | } 66 | 67 | /** 68 | * 返回当前元素,并把指针指向上一个元素 69 | * 70 | * @return 71 | */ 72 | public E prev() { 73 | if (node == null) { 74 | return null; 75 | } 76 | E e = node.item; 77 | node = node.prev; 78 | 79 | lastMode = MODE_PREV; 80 | return e; 81 | } 82 | 83 | /** 84 | * 删除队列中某一个元素 85 | * 86 | * @param e 87 | * @return 88 | */ 89 | public boolean remove(E e) { 90 | if (e == null) { 91 | for (Node x = first; x != null; x = x.next) { 92 | if (x.item == null) { 93 | unlink(x); 94 | return true; 95 | } 96 | } 97 | } else { 98 | for (Node x = first; x != null; x = x.next) { 99 | if (e.equals(x.item)) { 100 | unlink(x); 101 | return true; 102 | } 103 | } 104 | } 105 | 106 | size--; 107 | return true; 108 | } 109 | 110 | public E peek() { 111 | return node.item; 112 | } 113 | 114 | /** 115 | * 删除节点 116 | */ 117 | E unlink(Node x) { 118 | final E element = x.item; 119 | final Node next = x.next; 120 | final Node prev = x.prev; 121 | 122 | if (prev == x || next == x) { 123 | this.first = null; 124 | this.last = null; 125 | this.node = null; 126 | } 127 | 128 | next.prev = prev; 129 | prev.next = next; 130 | 131 | if ((element == null && this.node.item == null) || (element.equals(this.node.item))) { 132 | this.node = lastMode == MODE_NEXT ? this.node.next : this.node.prev; 133 | } 134 | 135 | x.item = null; 136 | x = null; 137 | size--; 138 | return element; 139 | } 140 | 141 | public int size() { 142 | return size; 143 | } 144 | 145 | 146 | /** 147 | * 节点类 148 | * 149 | * @param 150 | */ 151 | private static class Node { 152 | E item; 153 | Node next; 154 | Node prev; 155 | 156 | Node(Node prev, E element, Node next) { 157 | this.item = element; 158 | this.next = next; 159 | this.prev = prev; 160 | } 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/main/java/com/plexpt/chatgptonlinejava/util/SseHelper.java: -------------------------------------------------------------------------------- 1 | package com.plexpt.chatgptonlinejava.util; 2 | 3 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 4 | 5 | import lombok.experimental.UtilityClass; 6 | 7 | @UtilityClass 8 | public class SseHelper { 9 | 10 | 11 | public void complete(SseEmitter sseEmitter) { 12 | 13 | try { 14 | sseEmitter.complete(); 15 | } catch (Exception e) { 16 | 17 | } 18 | } 19 | 20 | public void send(SseEmitter sseEmitter, Object data) { 21 | 22 | try { 23 | sseEmitter.send(data); 24 | } catch (Exception e) { 25 | 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 666 3 | 4 | 5 | 6 | #????KEY????? at least 2 7 | keys: 8 | list: 9 | - sk-xxxxxxxxxxx 10 | - sk-xxxxxxxxxxx 11 | -------------------------------------------------------------------------------- /src/main/resources/static/boot.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Bootstrap demo 7 | 11 | 12 | 13 |

Hello, world!

14 | 17 | 18 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/resources/static/css/common.css: -------------------------------------------------------------------------------- 1 | html { 2 | line-height:1.15; 3 | -webkit-text-size-adjust:100%; 4 | } 5 | body { 6 | margin:0; 7 | } 8 | main { 9 | display:block; 10 | } 11 | h1 { 12 | font-size:2em; 13 | margin:0.67em 0; 14 | } 15 | hr { 16 | box-sizing:content-box; 17 | height:0; 18 | overflow:visible; 19 | } 20 | pre { 21 | font-family:monospace, monospace; 22 | font-size:1em; 23 | } 24 | img { 25 | height:auto; 26 | width:auto; 27 | max-width:500px; 28 | } 29 | a { 30 | background-color:transparent; 31 | } 32 | abbr[title] { 33 | border-bottom:none; 34 | text-decoration:underline; 35 | text-decoration:underline dotted; 36 | } 37 | b, strong { 38 | font-weight:bolder; 39 | } 40 | code, kbd, samp { 41 | font-family:monospace, monospace; 42 | font-size:1em; 43 | } 44 | small { 45 | font-size:80%; 46 | } 47 | sub, sup { 48 | font-size:75%; 49 | line-height:0; 50 | position:relative; 51 | vertical-align:baseline; 52 | } 53 | sub { 54 | bottom:-0.25em; 55 | } 56 | sup { 57 | top:-0.5em; 58 | } 59 | ul, li { 60 | list-style:none; 61 | } 62 | img { 63 | border-style:none; 64 | } 65 | button, input, optgroup, select, textarea { 66 | font-family:inherit; 67 | font-size:100%; 68 | line-height:1.15; 69 | margin:0; 70 | } 71 | button, input { 72 | overflow:visible; 73 | } 74 | button, select { 75 | text-transform:none; 76 | } 77 | button, [type="button"], [type="reset"], [type="submit"] { 78 | -webkit-appearance:button; 79 | } 80 | button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { 81 | border-style:none; 82 | padding:0; 83 | } 84 | button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { 85 | outline:1px dotted ButtonText; 86 | } 87 | fieldset { 88 | padding:0.35em 0.75em 0.625em; 89 | } 90 | legend { 91 | box-sizing:border-box; 92 | color:inherit; 93 | display:table; 94 | max-width:100%; 95 | padding:0; 96 | white-space:normal; 97 | } 98 | progress { 99 | vertical-align:baseline; 100 | } 101 | textarea { 102 | overflow:auto; 103 | } 104 | [type="checkbox"], [type="radio"] { 105 | box-sizing:border-box; 106 | padding:0; 107 | } 108 | [type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { 109 | height:auto; 110 | } 111 | [type="search"] { 112 | -webkit-appearance:textfield; 113 | outline-offset:-2px; 114 | } 115 | [type="search"]::-webkit-search-decoration { 116 | -webkit-appearance:none; 117 | } 118 | ::-webkit-file-upload-button { 119 | -webkit-appearance:button; 120 | font:inherit; 121 | } 122 | details { 123 | display:block; 124 | } 125 | summary { 126 | display:list-item; 127 | } 128 | template { 129 | display:none; 130 | } 131 | [hidden] { 132 | display:none; 133 | } 134 | body { 135 | font:14px / 1.5 Helvetica Neue, Helvetica, Arial, Hiragino Sans GB, Hiragino Sans GB W3, Microsoft YaHei UI, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif; 136 | } 137 | body * { 138 | padding:0; 139 | margin:0; 140 | box-sizing:border-box; 141 | } 142 | body.whole-screen { 143 | position:relative; 144 | height:100vh; 145 | overflow:hidden; 146 | } 147 | [data-flex] { 148 | display:-webkit-box; 149 | display:-webkit-flex; 150 | display:-ms-flexbox; 151 | display:flex; 152 | } 153 | [data-flex~="flex:wrap"] { 154 | flex-wrap:wrap 155 | } 156 | [data-flex~="flex:nowrap"] { 157 | flex-wrap:nowrap 158 | } 159 | [data-flex] > * { 160 | display:block; 161 | } 162 | [data-flex] >[data-flex] { 163 | display:-webkit-box; 164 | display:-webkit-flex; 165 | display:-ms-flexbox; 166 | display:flex; 167 | } 168 | [data-flex~="dir:left"] { 169 | -webkit-box-orient:horizontal; 170 | -webkit-box-direction:normal; 171 | -webkit-flex-direction:row; 172 | -ms-flex-direction:row; 173 | flex-direction:row; 174 | } 175 | [data-flex~="dir:right"] { 176 | -webkit-box-orient:horizontal; 177 | -webkit-box-direction:reverse; 178 | -webkit-flex-direction:row-reverse; 179 | -ms-flex-direction:row-reverse; 180 | flex-direction:row-reverse; 181 | -webkit-box-pack:end; 182 | } 183 | [data-flex~="dir:top"] { 184 | -webkit-box-orient:vertical; 185 | -webkit-box-direction:normal; 186 | -webkit-flex-direction:column; 187 | -ms-flex-direction:column; 188 | flex-direction:column; 189 | } 190 | [data-flex~="dir:bottom"] { 191 | -webkit-box-orient:vertical; 192 | -webkit-box-direction:reverse; 193 | -webkit-flex-direction:column-reverse; 194 | -ms-flex-direction:column-reverse; 195 | flex-direction:column-reverse; 196 | -webkit-box-pack:end; 197 | } 198 | [data-flex~="main:left"] { 199 | -webkit-box-pack:start; 200 | -webkit-justify-content:flex-start; 201 | -ms-flex-pack:start; 202 | justify-content:flex-start; 203 | } 204 | [data-flex~="main:right"] { 205 | -webkit-box-pack:end; 206 | -webkit-justify-content:flex-end; 207 | -ms-flex-pack:end; 208 | justify-content:flex-end; 209 | } 210 | [data-flex~="main:justify"] { 211 | -webkit-box-pack:justify; 212 | -webkit-justify-content:space-between; 213 | -ms-flex-pack:justify; 214 | justify-content:space-between; 215 | } 216 | [data-flex~="main:center"] { 217 | -webkit-box-pack:center; 218 | -webkit-justify-content:center; 219 | -ms-flex-pack:center; 220 | justify-content:center; 221 | } 222 | [data-flex~="cross:top"] { 223 | -webkit-box-align:start; 224 | -webkit-align-items:flex-start; 225 | -ms-flex-align:start; 226 | align-items:flex-start; 227 | } 228 | [data-flex~="cross:bottom"] { 229 | -webkit-box-align:end; 230 | -webkit-align-items:flex-end; 231 | -ms-flex-align:end; 232 | align-items:flex-end; 233 | } 234 | [data-flex~="cross:center"] { 235 | -webkit-box-align:center; 236 | -webkit-align-items:center; 237 | -ms-flex-align:center; 238 | align-items:center; 239 | } 240 | [data-flex~="cross:baseline"] { 241 | -webkit-box-align:baseline; 242 | -webkit-align-items:baseline; 243 | -ms-flex-align:baseline; 244 | align-items:baseline; 245 | } 246 | [data-flex~="cross:stretch"] { 247 | -webkit-box-align:stretch; 248 | -webkit-align-items:stretch; 249 | -ms-flex-align:stretch; 250 | align-items:stretch; 251 | } 252 | [data-flex~="box:mean"] > *, [data-flex~="box:first"] > *, [data-flex~="box:last"] > *, [data-flex~="box:justify"] > * { 253 | width:0; 254 | height:auto; 255 | -webkit-box-flex:1; 256 | -webkit-flex-grow:1; 257 | -ms-flex-positive:1; 258 | flex-grow:1; 259 | -webkit-flex-shrink:1; 260 | -ms-flex-negative:1; 261 | flex-shrink:1; 262 | } 263 | [data-flex~="box:first"] >:first-child, [data-flex~="box:last"] >:last-child, [data-flex~="box:justify"] >:first-child, [data-flex~="box:justify"] >:last-child { 264 | width:auto; 265 | -webkit-box-flex:0; 266 | -webkit-flex-grow:0; 267 | -ms-flex-positive:0; 268 | flex-grow:0; 269 | -webkit-flex-shrink:0; 270 | -ms-flex-negative:0; 271 | flex-shrink:0; 272 | } 273 | [data-flex~="dir:top"][data-flex~="box:mean"] > *, [data-flex~="dir:top"][data-flex~="box:first"] > *, [data-flex~="dir:top"][data-flex~="box:last"] > *, [data-flex~="dir:top"][data-flex~="box:justify"] > *, [data-flex~="dir:bottom"][data-flex~="box:mean"] > *, [data-flex~="dir:bottom"][data-flex~="box:first"] > *, [data-flex~="dir:bottom"][data-flex~="box:last"] > *, [data-flex~="dir:bottom"][data-flex~="box:justify"] > * { 274 | width:auto; 275 | height:0; 276 | -webkit-box-flex:1; 277 | -webkit-flex-grow:1; 278 | -ms-flex-positive:1; 279 | flex-grow:1; 280 | -webkit-flex-shrink:1; 281 | -ms-flex-negative:1; 282 | flex-shrink:1; 283 | } 284 | [data-flex~="dir:top"][data-flex~="box:first"] >:first-child, [data-flex~="dir:top"][data-flex~="box:last"] >:last-child, [data-flex~="dir:top"][data-flex~="box:justify"] >:first-child, [data-flex~="dir:top"][data-flex~="box:justify"] >:last-child, [data-flex~="dir:bottom"][data-flex~="box:first"] >:first-child, [data-flex~="dir:bottom"][data-flex~="box:last"] >:last-child, [data-flex~="dir:bottom"][data-flex~="box:justify"] >:first-child[data-flex~="dir:bottom"][data-flex~="box:justify"] >:last-child { 285 | height:auto; 286 | -webkit-box-flex:0; 287 | -webkit-flex-grow:0; 288 | -ms-flex-positive:0; 289 | flex-grow:0; 290 | -webkit-flex-shrink:0; 291 | -ms-flex-negative:0; 292 | flex-shrink:0; 293 | } 294 | [data-flex-box="0"] { 295 | -webkit-box-flex:0; 296 | -webkit-flex-grow:0; 297 | -ms-flex-positive:0; 298 | flex-grow:0; 299 | -webkit-flex-shrink:0; 300 | -ms-flex-negative:0; 301 | flex-shrink:0; 302 | } 303 | [data-flex-box="1"] { 304 | -webkit-box-flex:1; 305 | -webkit-flex-grow:1; 306 | -ms-flex-positive:1; 307 | flex-grow:1; 308 | -webkit-flex-shrink:1; 309 | -ms-flex-negative:1; 310 | flex-shrink:1; 311 | } 312 | [data-flex-box="2"] { 313 | -webkit-box-flex:2; 314 | -webkit-flex-grow:2; 315 | -ms-flex-positive:2; 316 | flex-grow:2; 317 | -webkit-flex-shrink:2; 318 | -ms-flex-negative:2; 319 | flex-shrink:2; 320 | } 321 | [data-flex-box="3"] { 322 | -webkit-box-flex:3; 323 | -webkit-flex-grow:3; 324 | -ms-flex-positive:3; 325 | flex-grow:3; 326 | -webkit-flex-shrink:3; 327 | -ms-flex-negative:3; 328 | flex-shrink:3; 329 | } 330 | [data-flex-box="4"] { 331 | -webkit-box-flex:4; 332 | -webkit-flex-grow:4; 333 | -ms-flex-positive:4; 334 | flex-grow:4; 335 | -webkit-flex-shrink:4; 336 | -ms-flex-negative:4; 337 | flex-shrink:4; 338 | } 339 | [data-flex-box="5"] { 340 | -webkit-box-flex:5; 341 | -webkit-flex-grow:5; 342 | -ms-flex-positive:5; 343 | flex-grow:5; 344 | -webkit-flex-shrink:5; 345 | -ms-flex-negative:5; 346 | flex-shrink:5; 347 | } 348 | [data-flex-box="6"] { 349 | -webkit-box-flex:6; 350 | -webkit-flex-grow:6; 351 | -ms-flex-positive:6; 352 | flex-grow:6; 353 | -webkit-flex-shrink:6; 354 | -ms-flex-negative:6; 355 | flex-shrink:6; 356 | } 357 | [data-flex-box="7"] { 358 | -webkit-box-flex:7; 359 | -webkit-flex-grow:7; 360 | -ms-flex-positive:7; 361 | flex-grow:7; 362 | -webkit-flex-shrink:7; 363 | -ms-flex-negative:7; 364 | flex-shrink:7; 365 | } 366 | [data-flex-box="8"] { 367 | -webkit-box-flex:8; 368 | -webkit-flex-grow:8; 369 | -ms-flex-positive:8; 370 | flex-grow:8; 371 | -webkit-flex-shrink:8; 372 | -ms-flex-negative:8; 373 | flex-shrink:8; 374 | } 375 | [data-flex-box="9"] { 376 | -webkit-box-flex:9; 377 | -webkit-flex-grow:9; 378 | -ms-flex-positive:9; 379 | flex-grow:9; 380 | -webkit-flex-shrink:9; 381 | -ms-flex-negative:9; 382 | flex-shrink:9; 383 | } 384 | [data-flex-box="10"] { 385 | -webkit-box-flex:10; 386 | -webkit-flex-grow:10; 387 | -ms-flex-positive:10; 388 | flex-grow:10; 389 | -webkit-flex-shrink:10; 390 | -ms-flex-negative:10; 391 | flex-shrink:10; 392 | } 393 | input { 394 | text-rendering:auto; 395 | letter-spacing:normal; 396 | word-spacing:normal; 397 | line-height:normal; 398 | text-transform:none; 399 | text-indent:0px; 400 | text-shadow:none; 401 | display:inline-block; 402 | text-align:start; 403 | appearance:auto; 404 | cursor:text; 405 | margin:0em; 406 | padding:1px 2px; 407 | border:none; 408 | text-indent:0; 409 | background:transparent; 410 | resize:none; 411 | outline:none; 412 | -webkit-appearance:none; 413 | line-height:normal; 414 | } 415 | .input:focus { 416 | outline:none; 417 | border-color:none 418 | } 419 | html { 420 | --zhuluan-white-color:#fff; 421 | --zhuluan-black-3-color:#333; 422 | --zhuluan-black-6-color:#666; 423 | --zhuluan-black-80-color:#808080; 424 | --zhuluan-custom-e8-color:#e8e8e8; 425 | --zhuluan-custom-d8-color:#d8d8d8; 426 | --zhuluan-custom-b8-color:#b8b8b8; 427 | --zhuluan-custom-ff-color:#f5f6f7; 428 | --zhuluan-white-f2-color:#f2f2f2; 429 | --zhuluan-white-f5-color:#f5f5f5; 430 | --zhuluan-primary-color:#1781ea; 431 | --zhuluan-neighbor-color:#5A9AF9; 432 | --zhuluan-primary-color-hover:#3385ff; 433 | --zhuluan-primary-color-active:#096dd9; 434 | --zhuluan-primary-rgba-10:rgba(31, 130, 242, .08); 435 | --zhuluan-primary-rgba-20:rgba(31, 130, 242, .2); 436 | --zhuluan-border-color:#e2e2e2; 437 | --zhuluan-border-rgba-4:rgba(225, 225, 225, .4); 438 | --zhuluan-warning-color:#FD6A53; 439 | --zhuluan-warning-rgba-10:rgba(253, 106, 83, .08); 440 | --zhuluan-warning-rgba-20:rgba(253, 106, 83, .2); 441 | --zhuluan-shadow-color:51 133 255; 442 | --zhuluan-switch-size:15px; 443 | --zhuluan-switch-box:30px; 444 | --zhuluan-primary-border-radius:5px; 445 | } 446 | body { 447 | background-color:var(--zhuluan-custom-ff-color) 448 | } 449 | @font-face { 450 | font-family:"iconfont"; 451 | src:url('../../css/iconfont.woff2') format('woff2'), url('../../css/iconfont.woff') format('woff'), url('../../css/iconfont.ttf') format('truetype'); 452 | } 453 | .input-group { 454 | position:relative; 455 | display:-ms-flexbox; 456 | display:flex; 457 | -ms-flex-wrap:wrap; 458 | flex-wrap:wrap; 459 | -ms-flex-align:stretch; 460 | align-items:stretch; 461 | width:100% 462 | } 463 | .input-group>.form-control, .input-group>.form-select { 464 | position:relative; 465 | -ms-flex:1 1 auto; 466 | flex:1 1 auto; 467 | width:1%; 468 | min-width:10 469 | } 470 | .form-control { 471 | display:block; 472 | width:100%; 473 | height:25px; 474 | padding:0px 5px; 475 | font-size:15px; 476 | line-height:1.42857143; 477 | color:#555; 478 | background-color:#fff; 479 | border:1px solid #000; 480 | border-radius:4px; 481 | -webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, .075); 482 | box-shadow:inset 0 1px 1px rgba(0, 0, 0, .075); 483 | -webkit-transition:border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; 484 | -o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s; 485 | transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s; 486 | } 487 | .iconfont { 488 | font-family:"iconfont" !important; 489 | font-size:16px; 490 | font-style:normal; 491 | -webkit-font-smoothing:antialiased; 492 | -moz-osx-font-smoothing:grayscale; 493 | } 494 | .icon-shuaxin:before { 495 | content:"\ec08"; 496 | } 497 | .icon-codelibrary:before { 498 | content:"\ebdb"; 499 | } 500 | .icon-jiance:before { 501 | content:"\e674"; 502 | } 503 | .icon-text-add:before { 504 | content:"\e614"; 505 | } 506 | .icon-close:before { 507 | content:"\e607"; 508 | } 509 | .icon-menu:before { 510 | content:"\e608"; 511 | } 512 | .icon-copy:before { 513 | content:"\e617"; 514 | } 515 | .icon-wuguan:before { 516 | content:"\ec5f"; 517 | } 518 | .icon-clear-all:before { 519 | content:"\e8b6"; 520 | } 521 | .icon-wenda:before { 522 | content:"\e60e"; 523 | } 524 | .icon-bangzhu:before { 525 | content:"\e600"; 526 | } 527 | .icon-baobiao:before { 528 | content:"\e601"; 529 | } 530 | .icon-rizhi:before { 531 | content:"\e602"; 532 | } 533 | .icon-pishi:before { 534 | content:"\e603"; 535 | } 536 | .icon-xinwen:before { 537 | content:"\e604"; 538 | } 539 | .icon-yewu:before { 540 | content:"\e605"; 541 | } 542 | .icon-tixing:before { 543 | content:"\e606"; 544 | } 545 | .switch-container { 546 | height:var(--zhuluan-switch-size); 547 | width:var(--zhuluan-switch-box); 548 | } 549 | .switch-container label, .switch-container label:before, .switch-container label:after { 550 | display:block; 551 | } 552 | .switch-container label { 553 | position:relative; 554 | background-color:var(--zhuluan-custom-e8-color); 555 | height:100%; 556 | width:100%; 557 | cursor:pointer; 558 | border-radius:25px; 559 | } 560 | .switch-container label:before, .switch-container label:after { 561 | content:''; 562 | } 563 | .switch-container label:before { 564 | border-radius:25px; 565 | height:100%; 566 | width:var(--zhuluan-switch-size); 567 | background-color:var(--zhuluan-white-color); 568 | opacity:1; 569 | box-shadow:1px 1px 1px 1px rgba(0, 0, 0, 0.08); 570 | -webkit-transition:all 0.2s; 571 | } 572 | .switch-container label:after { 573 | position:absolute; 574 | top:0; 575 | right:0; 576 | left:var(--zhuluan-switch-size); 577 | border-radius:25px; 578 | height:100%; 579 | width:var(--zhuluan-switch-size); 580 | background-color:white; 581 | opacity:0; 582 | box-shadow:1px 1px 1px 1px rgba(0, 0, 0, 0.08); 583 | transition:opacity 0.2s ease; 584 | } 585 | .switch:checked~label:after { 586 | opacity:1; 587 | } 588 | .switch:checked~label:before { 589 | opacity:0; 590 | } 591 | .switch:checked~label { 592 | background-color:var(--zhuluan-primary-color); 593 | } 594 | #tooltip { 595 | display:none; 596 | } 597 | #tooltip[data-show] { 598 | display:block; 599 | } 600 | .toast { 601 | top:50%; 602 | left:50%; 603 | position:fixed; 604 | -webkit-transform:translate(-50%, -50%); 605 | transform:translate(-50%, -50%); 606 | border-radius:5px; 607 | background:rgba(0, 0, 0, 0.6); 608 | color:white; 609 | -webkit-box-sizing:border-box; 610 | box-sizing:border-box; 611 | text-align:center; 612 | padding:10px 14px; 613 | z-index:999999; 614 | -webkit-animation-duration:500ms; 615 | animation-duration:500ms; 616 | } 617 | .toast.in { 618 | -webkit-animation-name:contentZoomIn; 619 | animation-name:contentZoomIn; 620 | } 621 | .toast .iconfont { 622 | font-size:30px; 623 | color:rgba(255, 255, 255, 0.8); 624 | margin-bottom:10px; 625 | display:block; 626 | } 627 | .toast .iconfont.icon-loading:before { 628 | display:block; 629 | -webkit-transform:rotate(360deg); 630 | animation:rotation 2.7s linear infinite; 631 | } 632 | .toast .text { 633 | text-align:center; 634 | max-width:300px; 635 | color:#fff; 636 | font-size:14px; 637 | } 638 | @-webkit-keyframes rotation { 639 | from { 640 | -webkit-transform:rotate(0deg); 641 | } 642 | to { 643 | -webkit-transform:rotate(360deg); 644 | } 645 | } 646 | @-webkit-keyframes contentZoomIn { 647 | 0% { 648 | -webkit-transform:translate(-50%, -70%); 649 | transform:translate(-50%, -70%); 650 | opacity:0; 651 | } 652 | 100% { 653 | -webkit-transform:translate(-50%, -50%); 654 | transform:translate(-50%, -50%); 655 | opacity:1; 656 | } 657 | } 658 | @keyframes contentZoomIn { 659 | 0% { 660 | opacity:0; 661 | } 662 | 100% { 663 | opacity:1; 664 | } 665 | } 666 | @-webkit-keyframes contentZoomOut { 667 | 0% { 668 | opacity:1; 669 | } 670 | 100% { 671 | opacity:0; 672 | } 673 | } 674 | @keyframes contentZoomOut { 675 | 0% { 676 | opacity:1; 677 | } 678 | 100% { 679 | opacity:0; 680 | } 681 | } 682 | a.unlinks-deco { 683 | color:var(--zhuluan-primary-color); 684 | text-decoration:none; 685 | } 686 | .layout-header { 687 | height:50px; 688 | background-color:var(--zhuluan-white-color) 689 | } 690 | .layout-header .logo .links { 691 | cursor:pointer; 692 | display:block; 693 | } 694 | .layout-header .logo .links img { 695 | height:100%; 696 | } 697 | .layout-header .icon-menu { 698 | display:none 699 | } 700 | .layout-header .nav .list { 701 | font-size:16px; 702 | font-weight:600; 703 | margin-left:32px; 704 | } 705 | .layout-header .nav .list .links { 706 | color:var(--zhuluan-black-6-color); 707 | transition:all .4s 708 | } 709 | .layout-header .nav .list.active .links, .layout-header .nav .list:hover .links, .article-box .links:hover { 710 | color:var(--zhuluan-primary-color) 711 | } 712 | .layout-header .nav .nav-btn { 713 | padding-left:35px; 714 | } 715 | .layout-header .nav .btn { 716 | padding:6px 12px; 717 | margin:0 0 0 14px; 718 | font-size:12px; 719 | font-weight:400; 720 | line-height:1.42857143; 721 | text-align:center; 722 | white-space:nowrap; 723 | vertical-align:middle; 724 | touch-action:manipulation; 725 | cursor:pointer; 726 | border-radius:var(--zhuluan-primary-border-radius); 727 | color:var(--zhuluan-black-3-color); 728 | background-color:var(--zhuluan-white-f2-color); 729 | transition:all .4s; 730 | } 731 | .layout-header .nav .btn.sign { 732 | color:var(--zhuluan-white-color); 733 | background-color:var(--zhuluan-primary-color); 734 | background:linear-gradient(90deg, var(--zhuluan-primary-color), var(--zhuluan-primary-color-hover)); 735 | box-shadow:0 4px 8px 0 rgb(var(--zhuluan-shadow-color) / 12%); 736 | } 737 | .layout-header .nav .btn:hover { 738 | opacity:.8; 739 | box-shadow:none 740 | } 741 | .layout-content { 742 | padding:22px 0 50px; 743 | background-color:#343541; 744 | } 745 | .layout-bar { 746 | font-size:14px; 747 | color:var(--zhuluan-black-80-color) 748 | } 749 | .layout-bar .num span { 750 | margin:0 6px; 751 | font-size:24px; 752 | font-weight:300; 753 | font-family:Open Sans, Arial, sans-serif; 754 | } 755 | .layout-bar .btn { 756 | width:100px; 757 | margin-left:14px; 758 | text-align:center; 759 | height:36px; 760 | line-height:36px; 761 | font-size:14px; 762 | border-radius:var(--zhuluan-primary-border-radius); 763 | color:var(--zhuluan-primary-color); 764 | cursor:pointer; 765 | transition:all .4s; 766 | } 767 | .layout-bar .balance { 768 | width:100px; 769 | margin-left:14px; 770 | text-align:center; 771 | height:24px; 772 | line-height:24px; 773 | font-size:14px; 774 | border-radius:var(--zhuluan-primary-border-radius); 775 | color:var(--zhuluan-primary-color); 776 | cursor:pointer; 777 | transition:all .4s; 778 | } 779 | 780 | .layout-bar .layout-bar-left { 781 | margin-right:-14px; 782 | } 783 | .layout-bar .layout-bar-left .btn { 784 | margin:0 14px 0 0 785 | } 786 | .layout-bar .btn .iconfont { 787 | margin-right:3px; 788 | } 789 | .layout-bar .btn .iconfont.icon-wuguan { 790 | margin-right:5px; 791 | } 792 | .layout-bar .btn .iconfont.icon-text-add { 793 | margin-right:5px; 794 | font-size:17px; 795 | } 796 | .layout-bar .bright-btn { 797 | color:var(--zhuluan-white-color); 798 | background-color:var(--zhuluan-primary-color); 799 | background:linear-gradient(90deg, var(--zhuluan-primary-color), var(--zhuluan-primary-color-hover)); 800 | box-shadow:0 4px 8px 0 var(--zhuluan-shadow-color); 801 | } 802 | .precast-block { 803 | padding:20px 12px; 804 | min-height:50px; 805 | border:1px solid; 806 | } 807 | .precast-block .title { 808 | width:65px; 809 | text-indent:10px; 810 | color:var(--zhuluan-black-80-color) 811 | } 812 | .kw-text { 813 | padding-top:10px; 814 | } 815 | #kw-box { 816 | margin-right:20px; 817 | } 818 | .kw-btn.btn { 819 | line-height:100%; 820 | color:var(--zhuluan-primary-color); 821 | cursor:pointer; 822 | } 823 | .kw-btn.btn .iconfont { 824 | margin-right:4px; 825 | } 826 | .precast-block .box { 827 | flex:1 828 | } 829 | #kw-target-box { 830 | border-radius:var(--zhuluan-primary-border-radius) var(--zhuluan-primary-border-radius) 0 0; 831 | -webkit-border-radius:; 832 | -moz-border-radius:; 833 | -ms-border-radius:; 834 | -o-border-radius:; 835 | } 836 | #kw-target-box #kw-target { 837 | display:block; 838 | height:36px; 839 | width:100%; 840 | padding-left:13px; 841 | font-size:16px; 842 | font-weight:500; 843 | color:#fff; 844 | } 845 | #kw-target::-webkit-input-placeholder { 846 | color:var(--zhuluan-custom-b8-color) 847 | } 848 | #kw-tags { 849 | border-top:none; 850 | height:28px; 851 | margin-bottom:-6px; 852 | } 853 | #kw-tags #tags-clear-all { 854 | margin-left:10px; 855 | } 856 | #kw-box { 857 | margin-bottom:-8px; 858 | } 859 | #kw-list { 860 | font-weight:500; 861 | font-size:12px; 862 | color:var(--zhuluan-black-80-color) 863 | } 864 | #xiezuo-h1 { 865 | color:var(--zhuluan-black-3-color); 866 | line-height:160%; 867 | margin-bottom:12px 868 | } 869 | #kw-list .kw, .locked-kw-tags .tag { 870 | display:inline-block; 871 | padding:6px 25px 4px 10px; 872 | border-radius:22px; 873 | margin:0 0 8px 8px; 874 | word-break:keep-all; 875 | position:relative; 876 | color:var(--zhuluan-primary-color); 877 | background-color:var(--zhuluan-primary-rgba-10); 878 | transition:all 0.4s 879 | } 880 | #kw-list .kw .icon-close, .locked-kw-tags .tag .icon-close { 881 | cursor:pointer; 882 | } 883 | #kw-list .kw:hover, .locked-kw-tags .tag:hover { 884 | background-color:var(--zhuluan-primary-rgba-20); 885 | } 886 | #kw-list .kw .iconfont, .locked-kw-tags .tag .iconfont { 887 | font-size:12px; 888 | position:absolute; 889 | right:8px; 890 | top:50%; 891 | transform:translateY(-50%) 892 | } 893 | .article .is-created-none, .article:not(.created) .is-created-block { 894 | display:none 895 | } 896 | .article:not(.created) .is-created-none, .article .is-created-block { 897 | display:block 898 | } 899 | .layout-bar .line-btn { 900 | color:var(--zhuluan-primary-color); 901 | } 902 | .article.created .xiezuo-header { 903 | border-radius:0; 904 | border-bottom:none 905 | } 906 | .layout-bar .line-btn:hover { 907 | box-shadow:0 4px 8px -2px rgb(51 51 51 / 8%) 908 | } 909 | .layout-bar .bright-btn:hover { 910 | background-color:var(--zhuluan-neighbor-color); 911 | box-shadow:none; 912 | opacity:.8 913 | } 914 | .magnitude #word-count { 915 | margin-right:4px 916 | } 917 | .magnitude #word-count .warning { 918 | color:var(--zhuluan-warning-color) 919 | } 920 | .article { 921 | width:100%; 922 | position:relative; 923 | } 924 | .article .creating-loading { 925 | display:none; 926 | position:absolute; 927 | z-index:10008; 928 | left:0; 929 | top:0; 930 | right:0; 931 | bottom:0; 932 | background-color:rgba(52, 53, 65, .68) 933 | } 934 | .article .creating-loading.isLoading { 935 | display:flex 936 | } 937 | .article .creating-loading .tips { 938 | margin-top:10px; 939 | color:var(--zhuluan-primary-color) 940 | } 941 | .article .layout-article { 942 | min-height:420px; 943 | border-radius:0 0 var(--zhuluan-primary-border-radius) var(--zhuluan-primary-border-radius); 944 | border:1px solid var(--zhuluan-border-color); 945 | background-color:var(--zhuluan-white-color); 946 | position:relative; 947 | } 948 | .layout-article .w-e-toolbar, .layout-article .w-e-text-container { 949 | border:none !important 950 | } 951 | .layout-article .w-e-toolbar { 952 | border-bottom:1px solid var(--zhuluan-border-color) !important; 953 | } 954 | .article-footer-bar { 955 | padding:12px 12px 6px; 956 | height:15px; 957 | color:var(--zhuluan-black-80-color) 958 | } 959 | .article-footer-bar .switch-container { 960 | margin-right:8px 961 | } 962 | .article-footer-bar .magnitude { 963 | text-align:right; 964 | font-size:14px; 965 | } 966 | .bar-fast-tool .iconfont { 967 | color:var(--zhuluan-custom-d8-color); 968 | transition:all .4s; 969 | cursor:pointer; 970 | } 971 | .bar-fast-tool .iconfont:hover { 972 | color:var(--zhuluan-primary-color) 973 | } 974 | #think-kw { 975 | position:absolute; 976 | left:0; 977 | top:0; 978 | width:100%; 979 | border-top:1px solid var(--zhuluan-border-color); 980 | background-color:var(--zhuluan-white-color); 981 | box-shadow:0 3px 5px rgba(30, 30, 30, .02) 982 | } 983 | #think-kw .list { 984 | padding:12px 10px; 985 | line-height:100%; 986 | border-bottom:1px solid var(--zhuluan-border-rgba-4); 987 | transition:all .4s; 988 | cursor:pointer; 989 | } 990 | #think-kw .list:hover, #think-kw .list.pitch { 991 | background-color:var(--zhuluan-warning-rgba-10) 992 | } 993 | #think-kw .list:last-child { 994 | border:none 995 | } 996 | #think-kw .list .text span { 997 | color:var(--zhuluan-warning-color) 998 | } 999 | #think-kw .list .num { 1000 | color:var(--zhuluan-black-80-color) 1001 | } 1002 | .bar-fast-tool .iconfont { 1003 | font-size:20px; 1004 | margin-left:5px; 1005 | } 1006 | .bar-fast-tool .magnitude { 1007 | margin-left:20px; 1008 | } 1009 | .layout-site-details { 1010 | padding-top:70px; 1011 | font-size:14px; 1012 | color:var(--zhuluan-black-6-color) 1013 | } 1014 | .layout-site-details h2 { 1015 | font-size:28px; 1016 | line-height:120%; 1017 | padding:0 20px; 1018 | margin:0 0 60px; 1019 | font-weight:500; 1020 | text-align:center; 1021 | } 1022 | .layout-site-details p { 1023 | line-height:200%; 1024 | } 1025 | .locked-kw { 1026 | position:relative; 1027 | border:1px solid var(--zhuluan-border-color); 1028 | border-top:none; 1029 | border-bottom:none; 1030 | } 1031 | .locked-kw .locked-kw-tags { 1032 | display:block; 1033 | width:100%; 1034 | border-color:var(--zhuluan-white-color); 1035 | border-radius:var(--zhuluan-primary-border-radius); 1036 | background-color:var(--zhuluan-white-color); 1037 | overflow:hidden; 1038 | } 1039 | .locked-kw-tags .tag { 1040 | margin:0 7px 7px 0; 1041 | font-size:12px; 1042 | } 1043 | .locked-kw .locked-kw-tags #locked-kw_addTag { 1044 | padding-left:15px; 1045 | padding-bottom:15px; 1046 | } 1047 | .locked-kw .locked-kw-tags #clear-all, #kw-tags #tags-clear-all { 1048 | color:var(--zhuluan-black-80-color); 1049 | font-size:12px; 1050 | cursor:pointer; 1051 | transition:all .4s 1052 | } 1053 | .locked-kw .locked-kw-tags #clear-all:hover, #kw-tags #tags-clear-all:hover { 1054 | color:var(--zhuluan-black-3-color); 1055 | } 1056 | .locked-kw .locked-kw-tags .tags_clear { 1057 | display:block; 1058 | margin-bottom:-7px 1059 | } 1060 | .locked-kw #locked-kw_tag { 1061 | font-size:12px; 1062 | font-weight:normal; 1063 | padding:8px 0 1064 | } 1065 | .footer { 1066 | padding-bottom:15px 1067 | } 1068 | .footer p { 1069 | text-align:center; 1070 | margin-bottom:8px 1071 | } 1072 | .footer p, .footer .links { 1073 | color:var(--zhuluan-black-80-color) 1074 | } 1075 | .footer .foot-menu { 1076 | font-size:16px 1077 | } 1078 | .footer .links { 1079 | margin:0 6px; 1080 | word-break:keep-all; 1081 | transition:all .4s 1082 | } 1083 | .footer .links:hover { 1084 | color:var(--zhuluan-black-3-color) 1085 | } 1086 | .content-list { 1087 | padding-bottom:20px; 1088 | } 1089 | .content-list li { 1090 | font-size:16px; 1091 | line-height:200%; 1092 | text-indent:32px; 1093 | color:var(--zhuluan-black-80-color) 1094 | } 1095 | .zhuluan-cms { 1096 | border-bottom:none; 1097 | border-top:none 1098 | } 1099 | .zhuluan-cms img { 1100 | width:30%; 1101 | vertical-align:top 1102 | } 1103 | .anchor-box { 1104 | position:fixed; 1105 | z-index:9999; 1106 | left:0; 1107 | top:0; 1108 | width:100vw; 1109 | height:100vh; 1110 | } 1111 | .anchor-box .anchor-content { 1112 | position:absolute; 1113 | width:420px; 1114 | height:220px; 1115 | left:50%; 1116 | top:50%; 1117 | padding:35px 25px; 1118 | transform:translate(-50%, -50%); 1119 | background-color:var(--zhuluan-white-color); 1120 | box-shadow:3px 3px 38px rgba(0, 0, 0, .1), -3px -3px 38px rgba(0, 0, 0, .1); 1121 | } 1122 | .anchor-box .anchor-content .icon-close { 1123 | position:absolute; 1124 | right:12px; 1125 | top:8px; 1126 | font-size:14px; 1127 | color:var(--zhuluan-black-80-color); 1128 | cursor:pointer; 1129 | } 1130 | .anchor-box .anchor-content .h4 { 1131 | margin-bottom:12px; 1132 | font-size:15px; 1133 | } 1134 | .anchor-box .anchor-content .h4 span { 1135 | color:var(--zhuluan-black-80-color); 1136 | margin-left:10px; 1137 | font-size:12px 1138 | } 1139 | .anchor-box .anchor-content em { 1140 | font-style:normal; 1141 | } 1142 | .anchor-box .anchor-content em.warning { 1143 | color:var(--zhuluan-warning-color); 1144 | } 1145 | .anchor-content .input-list { 1146 | margin-bottom:10px; 1147 | } 1148 | .anchor-content .input { 1149 | border:solid 1px var(--zhuluan-border-color); 1150 | padding:8px 10px; 1151 | width:100%; 1152 | border-radius:2px; 1153 | display:block; 1154 | } 1155 | .anchor-content .input:focus { 1156 | border-color:var(--zhuluan-primary-color); 1157 | } 1158 | .anchor-content .btn-box { 1159 | color:var(--zhuluan-black-80-color); 1160 | } 1161 | .anchor-content .btn-box .btn { 1162 | margin-left:12px; 1163 | cursor:pointer; 1164 | transition:all .4s 1165 | } 1166 | .anchor-content .btn-box .btn:hover { 1167 | opacity:.6 1168 | } 1169 | .anchor-content .btn-box .btn.add { 1170 | color:var(--zhuluan-primary-color); 1171 | } 1172 | .switch-bar .tips { 1173 | margin-left:10px; 1174 | } 1175 | .anchor-box { 1176 | position:absolute; 1177 | z-index:99998; 1178 | left:0; 1179 | top:0; 1180 | width:100%; 1181 | height:100%; 1182 | background-color:rgba(255, 255, 255, .5); 1183 | } 1184 | .banner { 1185 | padding-top:40px; 1186 | height:460px; 1187 | } 1188 | .banner .images { 1189 | width:582px; 1190 | height:424px; 1191 | } 1192 | .banner .images img { 1193 | width:100%; 1194 | height:auto; 1195 | } 1196 | .banner .title { 1197 | padding-left:55px; 1198 | } 1199 | .banner h2, .title h2 { 1200 | font-size:26px; 1201 | font-weight:normal; 1202 | padding-bottom:10px; 1203 | } 1204 | .banner p { 1205 | margin-top:6px; 1206 | color:var(--zhuluan-black-6-color) 1207 | } 1208 | .banner .btn { 1209 | display:block; 1210 | margin-top:22px; 1211 | width:120px; 1212 | height:42px; 1213 | line-height:42px; 1214 | border-radius:5px; 1215 | text-align:center; 1216 | color:var(--zhuluan-white-color); 1217 | font-size:16px; 1218 | background-color:var(--zhuluan-primary-color); 1219 | box-shadow:0 6px 18px 0 rgb(var(--zhuluan-shadow-color) / 20%); 1220 | cursor:pointer; 1221 | transition:all .4s 1222 | } 1223 | .banner .btn .iconfont { 1224 | font-size:18px; 1225 | margin-right:5px; 1226 | } 1227 | .banner .btn:hover { 1228 | background-color:var(--zhuluan-primary-color-hover); 1229 | box-shadow:none 1230 | } 1231 | .xiezuo { 1232 | background-color:var(--zhuluan-white-color); 1233 | padding:65px 0; 1234 | } 1235 | .xiezuo .title { 1236 | text-align:center; 1237 | } 1238 | .xiezuo .title h2 { 1239 | padding-bottom:6px; 1240 | } 1241 | .xiezuo .title p { 1242 | color:var(--zhuluan-black-80-color) 1243 | } 1244 | .xiezuo .content { 1245 | margin-left:-20px; 1246 | box-sizing:border-box; 1247 | width:100%; 1248 | } 1249 | .xiezuo .list .icon, .xiezuo .list .icon img { 1250 | margin:0 auto; 1251 | width:120px; 1252 | height:120px; 1253 | } 1254 | .xiezuo .list .icon { 1255 | margin-bottom:12px; 1256 | } 1257 | .xiezuo .list .icon img { 1258 | opacity:.9 1259 | } 1260 | .xiezuo .content .list { 1261 | border:1px solid var(--zhuluan-custom-ff-color); 1262 | border-radius:5px; 1263 | padding:25px 10px; 1264 | text-align:center; 1265 | width:calc(19.999999% - 22px); 1266 | margin:0 0 20px 20px; 1267 | box-sizing:border-box; 1268 | } 1269 | .xiezuo .list a { 1270 | display:block; 1271 | } 1272 | .xiezuo .content .list:last-child { 1273 | display:none; 1274 | } 1275 | .xiezuo .content .list .h3 { 1276 | font-size:24px; 1277 | font-weight:400; 1278 | color:var(--zhuluan-black-3-color) 1279 | } 1280 | .xiezuo .content .list p { 1281 | font-family:sans-serif; 1282 | font-size:18px; 1283 | color:var(--zhuluan-black-80-color); 1284 | text-transform:uppercase 1285 | } 1286 | .xiezuo .content .list, .xiezuo .content .list img, .xiezuo .content .list .h3, .xiezuo .content .list p { 1287 | transition:all .35s 1288 | } 1289 | .xiezuo .content .list:hover { 1290 | border-style:dashed; 1291 | border-color:var(--zhuluan-primary-color) 1292 | } 1293 | .xiezuo .content .list:hover img { 1294 | opacity:.6 1295 | } 1296 | .xiezuo .content .list:hover .h3 { 1297 | color:var(--zhuluan-primary-color) 1298 | } 1299 | .xiezuo .content .list:hover p { 1300 | color:var(--zhuluan-black-3-color) 1301 | } 1302 | .relevance { 1303 | padding-top:50px; 1304 | } 1305 | .relevance .ve-clever { 1306 | margin-left:-20px; 1307 | padding-top:25px; 1308 | } 1309 | .relevance .list { 1310 | border:1px dashed var(--zhuluan-custom-ff-color); 1311 | margin:0 0 20px 20px; 1312 | width:calc(24.999999% - 22px) 1313 | } 1314 | .relevance .list dt { 1315 | padding:14px; 1316 | border-bottom:1px dashed var(--zhuluan-custom-ff-color); 1317 | text-align:center; 1318 | font-size:16px; 1319 | color:var(--zhuluan-black-6-color) 1320 | } 1321 | .relevance .list dd { 1322 | padding:20px 0; 1323 | text-align:center; 1324 | color:var(--zhuluan-black-80-color) 1325 | } 1326 | .relevance .list dd p { 1327 | width:49.99999%; 1328 | padding:8px 35px; 1329 | } 1330 | .xiezuo-header { 1331 | text-align:center; 1332 | padding:25px 18px; 1333 | border-radius:var(--zhuluan-primary-border-radius); 1334 | -webkit-border-radius:var(--zhuluan-primary-border-radius); 1335 | -moz-border-radius:var(--zhuluan-primary-border-radius); 1336 | -ms-border-radius:var(--zhuluan-primary-border-radius); 1337 | -o-border-radius:var(--zhuluan-primary-border-radius); 1338 | } 1339 | .xiezuo-header span { 1340 | color:var(--zhuluan-black-80-color) 1341 | } 1342 | .xiezuo-header h3 { 1343 | padding:10px; 1344 | font-size:24px; 1345 | font-weight:normal; 1346 | } 1347 | .xiezuo-header li { 1348 | margin:0 2px 4px 2px; 1349 | } 1350 | .xiezuo-header li span { 1351 | position:relative; 1352 | margin-left:3px; 1353 | } 1354 | .xiezuo-header li span:before { 1355 | content:"#"; 1356 | color:var(--zhuluan-custom-d8-color); 1357 | margin-right:3px; 1358 | } 1359 | .container { 1360 | padding-right:15px; 1361 | padding-left:15px; 1362 | margin-right:auto; 1363 | margin-left:auto 1364 | } 1365 | @media (min-width:768px) { 1366 | .container { 1367 | width:750px 1368 | } 1369 | } 1370 | @media (min-width:992px) { 1371 | .container { 1372 | width:980px 1373 | } 1374 | } 1375 | @media (min-width:1200px) { 1376 | .container { 1377 | width:85%; 1378 | } 1379 | } 1380 | @media (max-width:1444px) and (min-width:993px) { 1381 | .xiezuo .content .list { 1382 | width:calc(33.333333% - 22px); 1383 | } 1384 | .xiezuo .content .list:last-child { 1385 | display:block 1386 | } 1387 | .relevance .list dd p { 1388 | padding:8px 15px; 1389 | } 1390 | } 1391 | @media (max-width:992px) { 1392 | .layout-header { 1393 | position; 1394 | relative; 1395 | } 1396 | .layout-header, .layout-header .logo, .layout-header .logo .links { 1397 | height:50px 1398 | } 1399 | .layout-header .logo .links img { 1400 | width:auto 1401 | } 1402 | .layout-header .icon-menu { 1403 | display:block 1404 | } 1405 | .header-nav .nav { 1406 | display:none; 1407 | } 1408 | .header-nav.open-menu .nav { 1409 | display:block; 1410 | position:absolute; 1411 | z-index:10008; 1412 | left:0; 1413 | right:0; 1414 | top:60px; 1415 | width:100%; 1416 | padding:15px 0 25px; 1417 | border-top:1px solid var(--zhuluan-custom-d8-color); 1418 | background-color:var(--zhuluan-white-color); 1419 | box-shadow:0 6px 6px rgba(30, 30, 30, .06) 1420 | } 1421 | .layout-header .nav .list { 1422 | height:35px; 1423 | line-height:35px; 1424 | } 1425 | .layout-header .nav .nav-btn { 1426 | padding-left:32px; 1427 | padding-top:15px; 1428 | } 1429 | .layout-header .nav .btn { 1430 | margin:0 14px 0 0 1431 | } 1432 | .layout-content { 1433 | padding:18px 0 32px 1434 | } 1435 | .article .creating-loading { 1436 | bottom:50%; 1437 | } 1438 | .layout-content .article { 1439 | -webkit-box-orient:vertical; 1440 | -webkit-box-direction:normal; 1441 | -webkit-flex-direction:column; 1442 | -ms-flex-direction:column; 1443 | flex-direction:column; 1444 | } 1445 | .article .layout-article { 1446 | width:100%; 1447 | margin-bottom:12px 1448 | } 1449 | .anchor-box .anchor-content { 1450 | width:92vw 1451 | } 1452 | .banner { 1453 | display:block; 1454 | padding-top:0; 1455 | height:400px; 1456 | } 1457 | .banner .images { 1458 | width:300px; 1459 | height:200px; 1460 | margin:0 auto 1461 | } 1462 | .banner h2, .title h2 { 1463 | font-size:24px 1464 | } 1465 | .banner .title { 1466 | padding-left:25px; 1467 | } 1468 | .xiezuo .content .list { 1469 | width:calc(49.999999% - 22px) 1470 | } 1471 | .xiezuo { 1472 | padding:40px 0 25px 1473 | } 1474 | .xiezuo .content { 1475 | padding-top:30px; 1476 | margin-left:-10px; 1477 | } 1478 | .xiezuo .content .list { 1479 | padding:15px 10px; 1480 | } 1481 | .xiezuo .content .list .icon, .xiezuo .content .list .icon img { 1482 | width:20vw; 1483 | height:20vw 1484 | } 1485 | .xiezuo .content .list .h3 { 1486 | font-size:20px; 1487 | } 1488 | .xiezuo .content .list p { 1489 | font-size:16px; 1490 | } 1491 | .layout-site-details { 1492 | padding-top:45px; 1493 | } 1494 | .layout-site-details h2 { 1495 | margin-bottom:40px; 1496 | } 1497 | .xiezuo .content .list:last-child { 1498 | display:block; 1499 | } 1500 | .relevance .list { 1501 | width:calc(49.999999% - 22px) 1502 | } 1503 | .relevance .list dd p { 1504 | padding:4px 5px; 1505 | } 1506 | .zhuluan-cms img { 1507 | width:80%; 1508 | } 1509 | #xiezuo-h1 { 1510 | font-size:18px; 1511 | } 1512 | #kw-tags { 1513 | height:auto; 1514 | } 1515 | #kw-box { 1516 | margin-bottom:4px; 1517 | } 1518 | .article-box { 1519 | margin:0 -10px; 1520 | padding:20px; 1521 | } 1522 | } 1523 | .semi-circle-spin { 1524 | position:relative; 1525 | width:35px; 1526 | height:35px; 1527 | overflow:hidden; 1528 | } 1529 | .semi-circle-spin:after { 1530 | content:''; 1531 | position:absolute; 1532 | border-width:0px; 1533 | border-radius:100%; 1534 | -webkit-animation:spin-rotate 0.6s 0s infinite linear; 1535 | animation:spin-rotate 0.6s 0s infinite linear; 1536 | background-image:-webkit-linear-gradient(transparent 0%, transparent 70%, var(--zhuluan-primary-color) 30%, var(--zhuluan-primary-color) 100%); 1537 | background-image:linear-gradient(transparent 0%, transparent 70%, var(--zhuluan-primary-color) 30%, var(--zhuluan-primary-color) 100%); 1538 | width:100%; 1539 | height:100%; 1540 | } 1541 | @-webkit-keyframes spin-rotate { 1542 | 0% { 1543 | -webkit-transform:rotate(0deg); 1544 | transform:rotate(0deg); 1545 | } 1546 | 50% { 1547 | -webkit-transform:rotate(180deg); 1548 | transform:rotate(180deg); 1549 | } 1550 | 100% { 1551 | -webkit-transform:rotate(360deg); 1552 | transform:rotate(360deg); 1553 | } 1554 | } 1555 | @keyframes spin-rotate { 1556 | 0% { 1557 | -webkit-transform:rotate(0deg); 1558 | transform:rotate(0deg); 1559 | } 1560 | 50% { 1561 | -webkit-transform:rotate(180deg); 1562 | transform:rotate(180deg); 1563 | } 1564 | 100% { 1565 | -webkit-transform:rotate(360deg); 1566 | transform:rotate(360deg); 1567 | } 1568 | } 1569 | .ball-clip-rotate-multiple { 1570 | position:relative; 1571 | } 1572 | .ball-clip-rotate-multiple > div { 1573 | -webkit-animation-fill-mode:both; 1574 | animation-fill-mode:both; 1575 | position:absolute; 1576 | left:0px; 1577 | top:0px; 1578 | border:2px solid #fff; 1579 | border-bottom-color:transparent; 1580 | border-top-color:transparent; 1581 | border-radius:100%; 1582 | height:35px; 1583 | width:35px; 1584 | -webkit-animation:rotate 1s 0s ease-in-out infinite; 1585 | animation:rotate 1s 0s ease-in-out infinite; 1586 | } 1587 | .ball-clip-rotate-multiple > div:last-child { 1588 | display:inline-block; 1589 | top:10px; 1590 | left:10px; 1591 | width:15px; 1592 | height:15px; 1593 | -webkit-animation-duration:0.5s; 1594 | animation-duration:0.5s; 1595 | border-color:#fff transparent #fff transparent; 1596 | -webkit-animation-direction:reverse; 1597 | animation-direction:reverse; 1598 | } -------------------------------------------------------------------------------- /src/main/resources/static/css/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlexPt/chatgpt-online-springboot/63df85db051e33c5dbd51b1ee69b95a34feec6c6/src/main/resources/static/css/iconfont.ttf -------------------------------------------------------------------------------- /src/main/resources/static/css/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlexPt/chatgpt-online-springboot/63df85db051e33c5dbd51b1ee69b95a34feec6c6/src/main/resources/static/css/iconfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/css/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlexPt/chatgpt-online-springboot/63df85db051e33c5dbd51b1ee69b95a34feec6c6/src/main/resources/static/css/iconfont.woff2 -------------------------------------------------------------------------------- /src/main/resources/static/css/wenda.css: -------------------------------------------------------------------------------- 1 | .logo-title { 2 | line-height:50px; 3 | color:#fff; 4 | text-decoration:none 5 | } 6 | .layout-header .logo .links { 7 | text-decoration:none; 8 | } 9 | .layout-header { 10 | background:#202123; 11 | } 12 | .layout-header .nav .list .links { 13 | color:#fff; 14 | } 15 | .xiezuo-header { 16 | max-height:calc(100vh - 30px); 17 | overflow-y:auto; 18 | min-height:200px; 19 | } 20 | #article-wrapper { 21 | min-height:calc(120vh - 550px); 22 | border:1px solid; 23 | border-radius:var(--zhuluan-primary-border-radius); 24 | } 25 | .article-box { 26 | min-height:calc(102vh - 68px) !important; 27 | position:relative; 28 | display:flex; 29 | flex-direction:column; 30 | justify-content:space-around; 31 | } 32 | #fixed-block { 33 | background-color:#40414F; 34 | bottom:20px; 35 | } 36 | #kw-target-box { 37 | border-radius:var(--zhuluan-primary-border-radius); 38 | -webkit-border-radius:var(--zhuluan-primary-border-radius); 39 | -moz-border-radius:var(--zhuluan-primary-border-radius); 40 | -ms-border-radius:var(--zhuluan-primary-border-radius); 41 | -o-border-radius:var(--zhuluan-primary-border-radius); 42 | } 43 | #popup { 44 | display:none; 45 | position:absolute; 46 | top:50%; 47 | left:50%; 48 | transform:translate(-50%, -50%); 49 | -webkit-transform:translate(-50%, -50%); 50 | -moz-transform:translate(-50%, -50%); 51 | -ms-transform:translate(-50%, -50%); 52 | -o-transform:translate(-50%, -50%); 53 | background-color:#fff; 54 | padding:20px; 55 | border:1px solid var(--zhuluan-border-color); 56 | width:70%; 57 | border-radius:var(--zhuluan-primary-border-radius); 58 | -webkit-border-radius:var(--zhuluan-primary-border-radius); 59 | -moz-border-radius:var(--zhuluan-primary-border-radius); 60 | -ms-border-radius:var(--zhuluan-primary-border-radius); 61 | -o-border-radius:var(--zhuluan-primary-border-radius); 62 | } 63 | #popup-close { 64 | font-size:24px; 65 | color:#666; 66 | float:right; 67 | cursor:pointer; 68 | } 69 | .popup-header { 70 | height:30px; 71 | } 72 | .pop-title { 73 | font-size:24px; 74 | } 75 | .popup-content { 76 | margin-top:20px; 77 | } 78 | .image-wrapper { 79 | display:flex; 80 | flex-direction:row; 81 | justify-content:space-around; 82 | } 83 | .image-wrapper img { 84 | width:40%; 85 | } 86 | .popup-footer { 87 | margin-top:10px; 88 | text-align:center; 89 | } 90 | #count-down { 91 | font-size:20px; 92 | color:red; 93 | } 94 | #sure-pay { 95 | margin-top:10px; 96 | display:block; 97 | width:120px; 98 | height:40px; 99 | line-height:40px; 100 | border:1px solid var(--zhuluan-border-color); 101 | border-radius:var(--zhuluan-primary-border-radius); 102 | background-color:#0188fb; 103 | color:#fff; 104 | -webkit-border-radius:var(--zhuluan-primary-border-radius); 105 | -moz-border-radius:var(--zhuluan-primary-border-radius); 106 | -ms-border-radius:var(--zhuluan-primary-border-radius); 107 | -o-border-radius:var(--zhuluan-primary-border-radius); 108 | margin-left:50%; 109 | transform:translateX(-50%); 110 | -webkit-transform:translateX(-50%); 111 | -moz-transform:translateX(-50%); 112 | -ms-transform:translateX(-50%); 113 | -o-transform:translateX(-50%); 114 | cursor:pointer; 115 | } 116 | li.article-title { 117 | background:#343541; 118 | padding:14px; 119 | color:#fff; 120 | font-size:15px; 121 | } 122 | li.article-content { 123 | background:#434654; 124 | padding:14px; 125 | color:#fff; 126 | font-size:15px; 127 | } 128 | .article .creating-loading { 129 | display:none; 130 | position:absolute; 131 | z-index:10008; 132 | left:0; 133 | top:0; 134 | right:0; 135 | bottom:0; 136 | background-color:rgba(52, 53, 65, .68); 137 | width:100%; 138 | height:100%; 139 | } 140 | .layout-content { 141 | padding:0 !important; 142 | } 143 | @media screen and (max-width:768px) { 144 | #popup { 145 | height:350px; 146 | } 147 | .image-wrapper img { 148 | width:92px; 149 | height:139px; 150 | } 151 | } 152 | pre { 153 | word-break:break-all; 154 | word-wrap:break-word; 155 | white-space:pre-wrap; 156 | } 157 | input { 158 | display:none; 159 | } 160 | label { 161 | display:block; 162 | width:40px; 163 | height:20px; 164 | border-radius:20px; 165 | background:rgb(164, 165, 179); 166 | border:1px solid #A4A5B3; 167 | cursor:pointer; 168 | position:relative; 169 | overflow:hidden; 170 | } 171 | label::before { 172 | display:block; 173 | content:''; 174 | width:16px; 175 | height:16px; 176 | border-radius:50%; 177 | background:white; 178 | position:absolute; 179 | left:1px; 180 | top:50%; 181 | transform:translateY(-50%); 182 | transition:all .3s; 183 | } 184 | label::after { 185 | display:block; 186 | content:''; 187 | width:0; 188 | height:100%; 189 | background:#202123; 190 | transition:all .3s; 191 | border-radius:10px; 192 | } 193 | input:checked + label::before { 194 | left:20px; 195 | } 196 | input:checked + label::after { 197 | width:100%; 198 | } -------------------------------------------------------------------------------- /src/main/resources/static/faq1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 常见问题 6 | 8 | 10 | 11 | 12 |
13 | 34 |
35 |
36 |
37 |
38 |

常见问题

39 |
40 |
41 |
42 |

43 | 48 |

49 |
50 | 51 |
53 |
54 | ChatGPT 55 | 是一个自然语言对话式人工智能助手,可以用自然语言的方式回答你的问题。它通过使用先进的深度学习算法来解析自然语言输入,并生成信息性回答。 56 |
57 |
58 |
59 | 60 |
61 |
62 |

63 | 68 |

69 |
70 |
72 |
73 | ChatGPT API 是一个 RESTful API,可让开发人员将 ChatGPT 功能集成到自己的应用程序中。API 74 | 用户可以将自然语言输入发送到 API,并获得返回的信息性响应。要使用 API,您需要一个 75 | API 密钥,可以通过在我们的网站上注册 ChatGPT 账户来获得。 76 |
77 |
78 |
79 | 80 |
81 |
82 |

83 | 88 |

89 |
90 |
92 |
93 | ChatGPT 94 | 的回答是由先进的深度学习算法生成的,这些算法经过了丰富的自然语言文本语料库的训练。因此,我们的响应通常非常准确和信息丰富。但是,请记住,AI 95 | 模型并不是完美的,有时会出现错误或提供不完整或不准确的回答。 96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/main/resources/static/faq2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 常见问题页面 6 | 8 | 9 | 10 | 11 |
12 |

常见问题

13 | 14 |
15 | 16 | 55 | 56 |
57 |
58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PlexPt/chatgpt-online-springboot/63df85db051e33c5dbd51b1ee69b95a34feec6c6/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ChatGPT Java 在线 6 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 58 | 59 | 60 |
61 | 62 |
63 | 64 | ChatGPT Java 在线 它几乎无所不能 65 | 66 | 67 | 75 |
76 | 77 |
78 | 79 |
80 |

保存的记录

81 |
    82 | 83 |
84 |
85 |
86 | 87 |
88 |
89 |

AI 模型列表

90 |
91 | 92 | 93 |
94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
ID创建时间
105 |
106 | 107 |
108 | 109 | 110 |
111 |

AI 聊天

112 |
113 |
114 | 115 | 116 |
117 |
118 | 119 | 121 |
122 |
123 | 124 | 125 |
126 |
127 | 128 | 130 | 139 |
140 |
141 | 142 | 143 |
144 |
145 | 146 | 148 |
149 |
150 | 151 | 154 |
155 |
156 |
157 | 158 | 159 |
160 |
161 | 162 | 163 | 164 | 165 |
166 | 169 |
170 |
171 |
172 |
173 | 174 |
175 | 176 |
177 | 178 |
179 |
180 |         
181 | 182 |
183 | 184 | 185 |
186 |
187 |
188 | 189 | 698 | 699 | 700 | -------------------------------------------------------------------------------- /src/main/resources/static/sse.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Minified by jsDelivr using Terser v5.19.2. 3 | * Original file: /npm/sse.js@2.5.0/lib/sse.js 4 | * 5 | * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files 6 | */ 7 | var SSE=function(t,s){if(!(this instanceof SSE))return new SSE(t,s);this.INITIALIZING=-1,this.CONNECTING=0,this.OPEN=1,this.CLOSED=2,this.url=t,s=s||{},this.headers=s.headers||{},this.payload=void 0!==s.payload?s.payload:"",this.method=s.method||(this.payload?"POST":"GET"),this.withCredentials=!!s.withCredentials,this.debug=!!s.debug,this.FIELD_SEPARATOR=":",this.listeners={},this.xhr=null,this.readyState=this.INITIALIZING,this.progress=0,this.chunk="",this.lastEventId="",this.addEventListener=function(t,s){void 0===this.listeners[t]&&(this.listeners[t]=[]),-1===this.listeners[t].indexOf(s)&&this.listeners[t].push(s)},this.removeEventListener=function(t,s){if(void 0===this.listeners[t])return;const e=[];this.listeners[t].forEach((function(t){t!==s&&e.push(t)})),0===e.length?delete this.listeners[t]:this.listeners[t]=e},this.dispatchEvent=function(t){if(!t)return!0;this.debug&&console.debug(t),t.source=this;const s="on"+t.type;return(!this.hasOwnProperty(s)||(this[s].call(this,t),!t.defaultPrevented))&&(!this.listeners[t.type]||this.listeners[t.type].every((function(s){return s(t),!t.defaultPrevented})))},this._setReadyState=function(t){const s=new CustomEvent("readystatechange");s.readyState=t,this.readyState=t,this.dispatchEvent(s)},this._onStreamFailure=function(t){const s=new CustomEvent("error");s.responseCode=this.xhr.status,s.data=t.currentTarget.response,this.dispatchEvent(s),this.close()},this._onStreamAbort=function(){this.dispatchEvent(new CustomEvent("abort")),this.close()},this._onStreamProgress=function(t){if(!this.xhr)return;if(200!==this.xhr.status)return void this._onStreamFailure(t);const s=this.xhr.responseText.substring(this.progress);this.progress+=s.length;const e=(this.chunk+s).split(/(\r\n\r\n|\r\r|\n\n)/g),i=e.pop();e.forEach(function(t){t.trim().length>0&&this.dispatchEvent(this._parseEventChunk(t))}.bind(this)),this.chunk=i},this._onStreamLoaded=function(t){this._onStreamProgress(t),this.dispatchEvent(this._parseEventChunk(this.chunk)),this.chunk=""},this._parseEventChunk=function(t){if(!t||0===t.length)return null;this.debug&&console.debug(t);const s={id:null,retry:null,data:null,event:null};t.split(/\n|\r\n|\r/).forEach(function(t){const e=t.indexOf(this.FIELD_SEPARATOR);let i,n;if(e>0){const s=" "===t[e+1]?2:1;i=t.substring(0,e),n=t.substring(e+s)}else{if(!(e<0))return;i=t,n=""}i in s&&("data"===i&&null!==s[i]?s.data+="\n"+n:s[i]=n)}.bind(this)),null!==s.id&&(this.lastEventId=s.id);const e=new CustomEvent(s.event||"message");return e.id=s.id,e.data=s.data||"",e.lastEventId=this.lastEventId,e},this._onReadyStateChange=function(){if(this.xhr)if(this.xhr.readyState===XMLHttpRequest.HEADERS_RECEIVED){const t={},s=this.xhr.getAllResponseHeaders().trim().split("\r\n");for(const e of s){const[s,...i]=e.split(":"),n=i.join(":").trim();t[s.trim().toLowerCase()]=t[s.trim().toLowerCase()]||[],t[s.trim().toLowerCase()].push(n)}const e=new CustomEvent("open");e.responseCode=this.xhr.status,e.headers=t,this.dispatchEvent(e),this._setReadyState(this.OPEN)}else this.xhr.readyState===XMLHttpRequest.DONE&&this._setReadyState(this.CLOSED)},this.stream=function(){if(!this.xhr){this._setReadyState(this.CONNECTING),this.xhr=new XMLHttpRequest,this.xhr.addEventListener("progress",this._onStreamProgress.bind(this)),this.xhr.addEventListener("load",this._onStreamLoaded.bind(this)),this.xhr.addEventListener("readystatechange",this._onReadyStateChange.bind(this)),this.xhr.addEventListener("error",this._onStreamFailure.bind(this)),this.xhr.addEventListener("abort",this._onStreamAbort.bind(this)),this.xhr.open(this.method,this.url);for(let t in this.headers)this.xhr.setRequestHeader(t,this.headers[t]);this.lastEventId.length>0&&this.xhr.setRequestHeader("Last-Event-ID",this.lastEventId),this.xhr.withCredentials=this.withCredentials,this.xhr.send(this.payload)}},this.close=function(){this.readyState!==this.CLOSED&&(this.xhr.abort(),this.xhr=null,this._setReadyState(this.CLOSED))},(void 0===s.start||s.start)&&this.stream()};"undefined"!=typeof exports&&(exports.SSE=SSE); 8 | --------------------------------------------------------------------------------