release
This commit is contained in:
@@ -1,66 +1,353 @@
|
||||
<?php
|
||||
require_once('../../includes/common.php');
|
||||
header("Content-Type:application/json;charset=UTF-8");
|
||||
session_start();
|
||||
|
||||
define('CACHE_DIR', __DIR__ . '/data/');
|
||||
define('CACHE_FILE', CACHE_DIR . 'questions.json');
|
||||
define('API_LOCK_FILE', CACHE_DIR . 'api.lock');
|
||||
define('API_INTERVAL', 5);
|
||||
|
||||
static $cacheData = null;
|
||||
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
$result = ['code' => 0, 'msg' => '', 'data' => null];
|
||||
|
||||
try {
|
||||
$stats = [];
|
||||
|
||||
$stats['count_category'] = $DB->count('category');
|
||||
$stats['count_site'] = $DB->count('site');
|
||||
$stats['count_apply'] = $DB->count('apply', array('reject' => 0));
|
||||
$stats['count_apply_reject'] = $DB->count('apply', array('reject' => 1));
|
||||
$stats['count_article'] = $DB->count('article');
|
||||
$stats['count_article_category'] = $DB->count('article_category');
|
||||
$stats['count_notice'] = $DB->count('notice');
|
||||
$stats['count_link'] = $DB->count('link');
|
||||
$stats['count_tags'] = 131;
|
||||
|
||||
$top_hits_day = $DB->find('site', 'id, name', array('date' => date("Y-m-d", time())), '`hits_day` desc');
|
||||
$top_hits_month = $DB->find('site', 'id, name', array('datem' => date("Y-m", time())), '`hits_month` desc');
|
||||
$top_hits_total = $DB->find('site', 'id, name', null, '`hits_total` desc');
|
||||
$top_like = $DB->find('site', 'id, name', null, '`like` desc');
|
||||
|
||||
$cumulative_hits_result = $DB->query("SELECT SUM(hits_total) as total FROM pre_site")->fetch();
|
||||
$cumulative_likes_result = $DB->query("SELECT SUM(`like`) as total FROM pre_site")->fetch();
|
||||
|
||||
$stats['cumulative_hits'] = $cumulative_hits_result['total'] ?? 0;
|
||||
$stats['cumulative_likes'] = $cumulative_likes_result['total'] ?? 0;
|
||||
|
||||
$stats['top_hits_day'] = $top_hits_day ? [
|
||||
'id' => $top_hits_day['id'],
|
||||
'name' => $top_hits_day['name']
|
||||
] : null;
|
||||
|
||||
$stats['top_hits_month'] = $top_hits_month ? [
|
||||
'id' => $top_hits_month['id'],
|
||||
'name' => $top_hits_month['name']
|
||||
] : null;
|
||||
|
||||
$stats['top_hits_total'] = $top_hits_total ? [
|
||||
'id' => $top_hits_total['id'],
|
||||
'name' => $top_hits_total['name']
|
||||
] : null;
|
||||
|
||||
$stats['top_like'] = $top_like ? [
|
||||
'id' => $top_like['id'],
|
||||
'name' => $top_like['name']
|
||||
] : null;
|
||||
|
||||
$stats['build_time'] = $conf['build_time'] ?? '';
|
||||
|
||||
echo json_encode([
|
||||
'ok' => true,
|
||||
'data' => $stats,
|
||||
'timestamp' => time()
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo json_encode([
|
||||
'ok' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
$action = $_REQUEST['action'] ?? 'question';
|
||||
$id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
|
||||
|
||||
switch($action){
|
||||
case 'question':
|
||||
$result['data'] = getQuestion($id ?? getCurrentId());
|
||||
break;
|
||||
case 'next':
|
||||
$result['data'] = getNextQuestionAuto();
|
||||
break;
|
||||
case 'fetch':
|
||||
$result['data'] = fetchNewQuestion();
|
||||
break;
|
||||
case 'answer':
|
||||
$result['data'] = checkAnswer($id, $_REQUEST['answer'] ?? '');
|
||||
break;
|
||||
case 'hint':
|
||||
$result['data'] = getHint($id);
|
||||
break;
|
||||
case 'list':
|
||||
$result['data'] = getQuestionList();
|
||||
break;
|
||||
case 'refresh':
|
||||
$result['data'] = refreshCache();
|
||||
break;
|
||||
case 'stats':
|
||||
$result['data'] = getStats();
|
||||
break;
|
||||
default:
|
||||
$result['code'] = 400;
|
||||
$result['msg'] = '未知操作';
|
||||
}
|
||||
|
||||
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
|
||||
|
||||
function getStats(){
|
||||
global $cacheData;
|
||||
$cache = loadCache();
|
||||
return [
|
||||
'total_questions' => count($cache),
|
||||
'cache_file_size' => file_exists(CACHE_FILE) ? round(filesize(CACHE_FILE) / 1024, 2) . ' KB' : 0,
|
||||
'last_updated' => file_exists(CACHE_FILE) ? date('Y-m-d H:i:s', filemtime(CACHE_FILE)) : 'N/A',
|
||||
'memory_usage' => round(memory_get_peak_usage(true) / 1024, 2) . ' KB'
|
||||
];
|
||||
}
|
||||
|
||||
function getQuestion($id){
|
||||
$cache = loadCache();
|
||||
if(empty($cache)){
|
||||
return ['error' => '没有题目数据'];
|
||||
}
|
||||
if($id < 0 || $id >= count($cache)){
|
||||
return ['error' => '题目不存在', 'total' => count($cache)];
|
||||
}
|
||||
$item = $cache[$id];
|
||||
return [
|
||||
'id' => $id,
|
||||
'total' => count($cache),
|
||||
'question' => $item['question_content'],
|
||||
'author' => $item['type']['person'] ?? '',
|
||||
'type' => $item['type']['type'] ?? '',
|
||||
'grade' => $item['type']['grade'] ?? '',
|
||||
'dynasty' => $item['type']['dynasty'] ?? '',
|
||||
'options' => formatOptions($item['option_answers'] ?? [])
|
||||
];
|
||||
}
|
||||
|
||||
function getNextQuestionAuto(){
|
||||
$cache = loadCache();
|
||||
if(empty($cache)){
|
||||
return ['error' => '没有题目数据'];
|
||||
}
|
||||
$total = count($cache);
|
||||
$currentId = getCurrentId();
|
||||
$nextId = $currentId + 1;
|
||||
if($nextId >= $total){
|
||||
$nextId = 0;
|
||||
}
|
||||
setCurrentId($nextId);
|
||||
$result = getQuestion($nextId);
|
||||
$result['prev_id'] = $currentId;
|
||||
return $result;
|
||||
}
|
||||
|
||||
function getCurrentId(){
|
||||
return isset($_SESSION['current_question_id']) ? intval($_SESSION['current_question_id']) : 0;
|
||||
}
|
||||
|
||||
function setCurrentId($id){
|
||||
$_SESSION['current_question_id'] = $id;
|
||||
}
|
||||
|
||||
function fetchNewQuestion(){
|
||||
$apiData = fetchFromBaiduApi();
|
||||
|
||||
if($apiData === null){
|
||||
$cache = loadCache();
|
||||
if(!empty($cache)){
|
||||
$randomId = array_rand($cache);
|
||||
$result = formatQuestionItem($randomId, $cache[$randomId], count($cache));
|
||||
$result['from_cache'] = true;
|
||||
$result['api_status'] = 'failed';
|
||||
return $result;
|
||||
}
|
||||
return ['error' => 'API请求失败且无本地缓存'];
|
||||
}
|
||||
|
||||
$cache = loadCache();
|
||||
$newCount = mergeToCache($apiData, $cache);
|
||||
|
||||
$randomItem = $apiData[array_rand($apiData)];
|
||||
$result = formatQuestionItem(null, $randomItem, count($cache));
|
||||
$result['from_cache'] = false;
|
||||
$result['new_questions'] = $newCount;
|
||||
$result['api_status'] = 'success';
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
function formatQuestionItem($id, $item, $total, $fromCache = false, $newCount = 0){
|
||||
return [
|
||||
'id' => $id,
|
||||
'total' => $total,
|
||||
'question' => $item['question_content'],
|
||||
'author' => $item['type']['person'] ?? '',
|
||||
'type' => $item['type']['type'] ?? '',
|
||||
'grade' => $item['type']['grade'] ?? '',
|
||||
'dynasty' => $item['type']['dynasty'] ?? '',
|
||||
'options' => formatOptions($item['option_answers'] ?? []),
|
||||
'from_cache' => $fromCache,
|
||||
'new_questions' => $newCount
|
||||
];
|
||||
}
|
||||
|
||||
function checkAnswer($id, $answer){
|
||||
$cache = loadCache();
|
||||
if(empty($cache)){
|
||||
return ['error' => '没有题目数据'];
|
||||
}
|
||||
if($id < 0 || $id >= count($cache)){
|
||||
return ['error' => '题目不存在'];
|
||||
}
|
||||
$item = $cache[$id];
|
||||
$correctAnswer = findAnswer($item['option_answers'] ?? []);
|
||||
$isCorrect = ($answer === $correctAnswer);
|
||||
|
||||
return [
|
||||
'id' => $id,
|
||||
'correct' => $isCorrect,
|
||||
'your_answer' => $answer,
|
||||
'correct_answer' => $correctAnswer,
|
||||
'next_id' => $id + 1,
|
||||
'has_next' => $id + 1 < count($cache)
|
||||
];
|
||||
}
|
||||
|
||||
function getHint($id){
|
||||
$cache = loadCache();
|
||||
if(empty($cache)){
|
||||
return ['error' => '没有题目数据'];
|
||||
}
|
||||
if($id < 0 || $id >= count($cache)){
|
||||
return ['error' => '题目不存在'];
|
||||
}
|
||||
$item = $cache[$id];
|
||||
return [
|
||||
'id' => $id,
|
||||
'hint' => '这是首描写' . ($item['type']['type'] ?? '') . '的诗,你在' . ($item['type']['grade'] ?? '') . '学过它。',
|
||||
'author' => $item['type']['person'] ?? '',
|
||||
'dynasty' => $item['type']['dynasty'] ?? ''
|
||||
];
|
||||
}
|
||||
|
||||
function getQuestionList(){
|
||||
$cache = loadCache();
|
||||
if(empty($cache)){
|
||||
return ['total' => 0, 'list' => []];
|
||||
}
|
||||
$list = [];
|
||||
foreach($cache as $i => $item){
|
||||
$list[] = [
|
||||
'id' => $i,
|
||||
'question' => mb_substr($item['question_content'], 0, 30, 'UTF-8') . '...',
|
||||
'author' => $item['type']['person'] ?? '',
|
||||
'dynasty' => $item['type']['dynasty'] ?? ''
|
||||
];
|
||||
}
|
||||
return ['total' => count($list), 'list' => $list];
|
||||
}
|
||||
|
||||
function refreshCache(){
|
||||
$apiData = fetchFromBaiduApi();
|
||||
if($apiData === null){
|
||||
return ['refreshed' => false, 'error' => 'API请求失败'];
|
||||
}
|
||||
$cache = loadCache();
|
||||
$newCount = mergeToCache($apiData, $cache);
|
||||
return ['refreshed' => true, 'total' => count($cache), 'new_questions' => $newCount];
|
||||
}
|
||||
|
||||
function loadCache(){
|
||||
global $cacheData;
|
||||
|
||||
if($cacheData !== null){
|
||||
return $cacheData;
|
||||
}
|
||||
|
||||
if(!file_exists(CACHE_FILE)){
|
||||
$cacheData = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = file_get_contents(CACHE_FILE);
|
||||
if($content === false){
|
||||
$cacheData = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
$data = json_decode($content, true);
|
||||
if(!is_array($data) || !isset($data['questions'])){
|
||||
$cacheData = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
$cacheData = $data['questions'];
|
||||
return $cacheData;
|
||||
}
|
||||
|
||||
function saveCache($questions){
|
||||
global $cacheData;
|
||||
|
||||
if(!is_dir(CACHE_DIR)){
|
||||
mkdir(CACHE_DIR, 0755, true);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'updated' => date('Y-m-d H:i:s'),
|
||||
'count' => count($questions),
|
||||
'questions' => $questions
|
||||
];
|
||||
|
||||
$tempFile = CACHE_FILE . '.tmp';
|
||||
$result = file_put_contents($tempFile, json_encode($data, JSON_UNESCAPED_UNICODE), LOCK_EX);
|
||||
|
||||
if($result !== false){
|
||||
rename($tempFile, CACHE_FILE);
|
||||
}
|
||||
|
||||
$cacheData = $questions;
|
||||
}
|
||||
|
||||
function fetchFromBaiduApi(){
|
||||
if(file_exists(API_LOCK_FILE)){
|
||||
$lockTime = intval(file_get_contents(API_LOCK_FILE));
|
||||
if(time() - $lockTime < API_INTERVAL){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents(API_LOCK_FILE, time());
|
||||
|
||||
$url = 'https://hanyu.baidu.com/hanyu/ajax/pingce_data';
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_CONNECTTIMEOUT => 3,
|
||||
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Accept: application/json',
|
||||
'Accept-Language: zh-CN,zh;q=0.9'
|
||||
]
|
||||
]);
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if($httpCode !== 200 || empty($response)){
|
||||
return null;
|
||||
}
|
||||
|
||||
$json = json_decode($response, true);
|
||||
if(!isset($json['data']) || !is_array($json['data'])){
|
||||
return null;
|
||||
}
|
||||
|
||||
return $json['data'];
|
||||
}
|
||||
|
||||
function mergeToCache($newData, &$existingCache){
|
||||
if(empty($newData)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
$existingKeys = [];
|
||||
foreach($existingCache as $item){
|
||||
$key = $item['question_content'] ?? '';
|
||||
$existingKeys[$key] = true;
|
||||
}
|
||||
|
||||
$newCount = 0;
|
||||
foreach($newData as $item){
|
||||
$key = $item['question_content'] ?? '';
|
||||
if(!isset($existingKeys[$key])){
|
||||
$existingCache[] = $item;
|
||||
$existingKeys[$key] = true;
|
||||
$newCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if($newCount > 0){
|
||||
saveCache($existingCache);
|
||||
}
|
||||
|
||||
return $newCount;
|
||||
}
|
||||
|
||||
function formatOptions($options){
|
||||
$result = [];
|
||||
foreach($options as $i => $opt){
|
||||
$result[] = [
|
||||
'index' => $i + 1,
|
||||
'content' => $opt['answer_content'] ?? ''
|
||||
];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
function findAnswer($options){
|
||||
foreach($options as $i => $opt){
|
||||
if(($opt['is_standard_answer'] ?? 0) === 1){
|
||||
return (string)($i + 1);
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
?>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@@ -15,6 +16,8 @@ class CareController extends GetxController {
|
||||
final RxBool isCareButtonVisible = false.obs;
|
||||
final RxInt _careNavigationIndex = 0.obs;
|
||||
|
||||
Timer? _autoHideTimer;
|
||||
|
||||
bool get isCareModeEnabled => _isCareModeEnabled.value;
|
||||
String get userType => _userType.value;
|
||||
bool get pinyinEnabled => _pinyinEnabled.value;
|
||||
@@ -77,6 +80,40 @@ class CareController extends GetxController {
|
||||
|
||||
void toggleCareButtonVisibility() {
|
||||
isCareButtonVisible.value = !isCareButtonVisible.value;
|
||||
|
||||
// 如果显示关怀开关,启动3秒自动隐藏定时器
|
||||
if (isCareButtonVisible.value) {
|
||||
_startAutoHideTimer();
|
||||
} else {
|
||||
_cancelAutoHideTimer();
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动自动隐藏定时器
|
||||
void _startAutoHideTimer() {
|
||||
_cancelAutoHideTimer();
|
||||
_autoHideTimer = Timer(const Duration(seconds: 3), () {
|
||||
isCareButtonVisible.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
/// 取消自动隐藏定时器
|
||||
void _cancelAutoHideTimer() {
|
||||
_autoHideTimer?.cancel();
|
||||
_autoHideTimer = null;
|
||||
}
|
||||
|
||||
/// 重置自动隐藏定时器(用户交互时调用)
|
||||
void resetAutoHideTimer() {
|
||||
if (isCareButtonVisible.value) {
|
||||
_startAutoHideTimer();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
_cancelAutoHideTimer();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
Future<void> switchCareNavigation(int index) async {
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controllers/shared_preferences_storage_controller.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import 'theme_controller.dart';
|
||||
|
||||
class DiscoverController extends GetxController {
|
||||
var categories = <String>[].obs;
|
||||
@@ -64,7 +65,8 @@ class DiscoverController extends GetxController {
|
||||
|
||||
Future<void> refreshContent() async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
Get.snackbar('提示', '内容已刷新');
|
||||
final themeController = Get.find<ThemeController>();
|
||||
Get.snackbar('提示', '内容已刷新', colorText: themeController.currentThemeColor);
|
||||
}
|
||||
|
||||
void toggleTips() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import 'theme_controller.dart';
|
||||
|
||||
class FavoritesController extends GetxController {
|
||||
var categories = ['全部', '点赞', '笔记', '推送', '每日一句'].obs;
|
||||
@@ -24,10 +25,12 @@ class FavoritesController extends GetxController {
|
||||
|
||||
Future<void> refreshContent() async {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
Get.snackbar('提示', '内容已刷新');
|
||||
final themeController = Get.find<ThemeController>();
|
||||
Get.snackbar('提示', '内容已刷新', colorText: themeController.currentThemeColor);
|
||||
}
|
||||
|
||||
void showFilterOptions(BuildContext context) {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
// 先获取当前值,避免在弹窗中使用 Obx
|
||||
final currentSortByTime = sortByTime.value;
|
||||
final currentLikesFirst = likesFirst.value;
|
||||
@@ -58,7 +61,8 @@ class FavoritesController extends GetxController {
|
||||
sortByTime.value = true;
|
||||
// 使用 Future.delayed 确保弹窗完全关闭后再显示 snackbar
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Get.snackbar('提示', '已按时间排序');
|
||||
final tc = Get.find<ThemeController>();
|
||||
Get.snackbar('提示', '已按时间排序', colorText: tc.currentThemeColor);
|
||||
});
|
||||
},
|
||||
),
|
||||
@@ -76,7 +80,8 @@ class FavoritesController extends GetxController {
|
||||
sortByTime.value = false;
|
||||
// 使用 Future.delayed 确保弹窗完全关闭后再显示 snackbar
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Get.snackbar('提示', '已按分类排序');
|
||||
final tc = Get.find<ThemeController>();
|
||||
Get.snackbar('提示', '已按分类排序', colorText: tc.currentThemeColor);
|
||||
});
|
||||
},
|
||||
),
|
||||
@@ -90,7 +95,12 @@ class FavoritesController extends GetxController {
|
||||
likesFirst.value = !likesFirst.value;
|
||||
// 使用 Future.delayed 确保弹窗完全关闭后再显示 snackbar
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
Get.snackbar('提示', likesFirst.value ? '点赞在前' : '笔记在前');
|
||||
final tc = Get.find<ThemeController>();
|
||||
Get.snackbar(
|
||||
'提示',
|
||||
likesFirst.value ? '点赞在前' : '笔记在前',
|
||||
colorText: tc.currentThemeColor,
|
||||
);
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
@@ -415,6 +415,7 @@ class HomeController extends GetxController with NetworkListenerMixin {
|
||||
'keywords': true,
|
||||
'introduction': true,
|
||||
};
|
||||
update(); // 立即通知UI更新,显示骨架屏
|
||||
|
||||
try {
|
||||
final offlineDataManager = OfflineDataManager();
|
||||
@@ -490,7 +491,7 @@ class HomeController extends GetxController with NetworkListenerMixin {
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟分步加载过程
|
||||
// 模拟分步加载过程 - 简化版,避免闪烁
|
||||
Future<void> simulateSectionLoading(PoetryData newPoetryData) async {
|
||||
// 记录浏览统计
|
||||
try {
|
||||
@@ -501,60 +502,27 @@ class HomeController extends GetxController with NetworkListenerMixin {
|
||||
// 忽略错误
|
||||
}
|
||||
|
||||
// 1. 加载标题区域
|
||||
var states = <String, bool>{};
|
||||
sectionLoadingStates.forEach((key, value) {
|
||||
states[key] = value;
|
||||
});
|
||||
states['title'] = false;
|
||||
sectionLoadingStates.assignAll(states);
|
||||
// 1. 立即更新所有数据
|
||||
poetryData.value = newPoetryData;
|
||||
update(); // 通知UI更新
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// 2. 加载诗句区域
|
||||
states = <String, bool>{};
|
||||
sectionLoadingStates.forEach((key, value) {
|
||||
states[key] = value;
|
||||
});
|
||||
states['name'] = false;
|
||||
sectionLoadingStates.assignAll(states);
|
||||
update(); // 通知UI更新
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// 3. 加载原文区域
|
||||
states = <String, bool>{};
|
||||
sectionLoadingStates.forEach((key, value) {
|
||||
states[key] = value;
|
||||
});
|
||||
states['content'] = false;
|
||||
sectionLoadingStates.assignAll(states);
|
||||
update(); // 通知UI更新
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// 4. 加载关键词区域
|
||||
states = <String, bool>{};
|
||||
sectionLoadingStates.forEach((key, value) {
|
||||
states[key] = value;
|
||||
});
|
||||
states['keywords'] = false;
|
||||
sectionLoadingStates.assignAll(states);
|
||||
keywordList.value = PoetryDataUtils.extractKeywords(newPoetryData);
|
||||
update(); // 通知UI更新
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
// 5. 加载译文区域
|
||||
states = <String, bool>{};
|
||||
sectionLoadingStates.forEach((key, value) {
|
||||
states[key] = value;
|
||||
});
|
||||
states['introduction'] = false;
|
||||
sectionLoadingStates.assignAll(states);
|
||||
starDisplay.value = PoetryDataUtils.getStarDisplay(newPoetryData);
|
||||
isLiked.value = false;
|
||||
errorMessage.value = '';
|
||||
update(); // 通知UI更新
|
||||
|
||||
// 2. 短暂延迟后,一次性关闭所有加载状态
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
|
||||
// 一次性关闭所有加载状态
|
||||
sectionLoadingStates.value = {
|
||||
'title': false,
|
||||
'content': false,
|
||||
'name': false,
|
||||
'keywords': false,
|
||||
'introduction': false,
|
||||
};
|
||||
update();
|
||||
|
||||
checkIfLiked();
|
||||
updateCurrentHistoryIndex();
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../../controllers/history_controller.dart';
|
||||
import '../../controllers/shared_preferences_storage_controller.dart';
|
||||
import '../isweb/wakelock_service.dart';
|
||||
import '../../views/profile/guide/tongji.dart';
|
||||
import 'theme_controller.dart';
|
||||
|
||||
class ProfileController extends GetxController with WidgetsBindingObserver {
|
||||
// 页面状态
|
||||
@@ -214,9 +215,14 @@ class ProfileController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// === 屏幕常亮相关方法 ===
|
||||
Future<void> toggleScreenWake(bool enable) async {
|
||||
final themeController = Get.find<ThemeController>();
|
||||
// Web 平台不支持 wakelock_plus
|
||||
if (kIsWeb) {
|
||||
Get.snackbar('提示', 'Web 平台不支持屏幕常亮功能');
|
||||
Get.snackbar(
|
||||
'提示',
|
||||
'Web 平台不支持屏幕常亮功能',
|
||||
colorText: themeController.currentThemeColor,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,10 +234,18 @@ class ProfileController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
if (enable) {
|
||||
await WakelockService.instance.enable();
|
||||
Get.snackbar('提示', '屏幕常亮已开启');
|
||||
Get.snackbar(
|
||||
'提示',
|
||||
'屏幕常亮已开启',
|
||||
colorText: themeController.currentThemeColor,
|
||||
);
|
||||
} else {
|
||||
await WakelockService.instance.disable();
|
||||
Get.snackbar('提示', '屏幕常亮已关闭');
|
||||
Get.snackbar(
|
||||
'提示',
|
||||
'屏幕常亮已关闭',
|
||||
colorText: themeController.currentThemeColor,
|
||||
);
|
||||
}
|
||||
|
||||
// 不再更新开关状态,由 UI 层直接控制
|
||||
@@ -324,6 +338,7 @@ class ProfileController extends GetxController with WidgetsBindingObserver {
|
||||
|
||||
// === 显示提示 ===
|
||||
void showSnackBar(String message) {
|
||||
Get.snackbar('提示', message);
|
||||
final themeController = Get.find<ThemeController>();
|
||||
Get.snackbar('提示', message, colorText: themeController.currentThemeColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ enum TransparencyLevel {
|
||||
class TapLiquidGlassController extends GetxController {
|
||||
SharedPreferences? _prefs;
|
||||
final _isEnabled = true.obs;
|
||||
final _transparencyLevel = TransparencyLevel.weak.obs;
|
||||
final _transparencyLevel = TransparencyLevel.medium.obs;
|
||||
|
||||
bool get isEnabled => _isEnabled.value;
|
||||
RxBool get isEnabledRx => _isEnabled;
|
||||
@@ -68,7 +68,7 @@ class TapLiquidGlassController extends GetxController {
|
||||
Future<void> _loadSettings() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
_isEnabled.value = _prefs?.getBool(AppConfig.keyTapLiquidGlass) ?? true;
|
||||
final levelIndex = _prefs?.getInt('tap_liquid_glass_transparency') ?? 0;
|
||||
final levelIndex = _prefs?.getInt('tap_liquid_glass_transparency') ?? 1;
|
||||
_transparencyLevel.value = TransparencyLevel
|
||||
.values[levelIndex.clamp(0, TransparencyLevel.values.length - 1)];
|
||||
}
|
||||
|
||||
@@ -138,6 +138,7 @@ class ThemeController extends GetxController {
|
||||
duration: const Duration(seconds: 2),
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
colorText: currentThemeColor,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -173,6 +174,7 @@ class ThemeController extends GetxController {
|
||||
duration: const Duration(seconds: 2),
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
colorText: currentThemeColor,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -266,6 +268,7 @@ class ThemeController extends GetxController {
|
||||
duration: const Duration(seconds: 2),
|
||||
margin: const EdgeInsets.all(16),
|
||||
borderRadius: 12,
|
||||
colorText: currentThemeColor,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user