add
This commit is contained in:
@@ -3,20 +3,27 @@ import 'package:asset_assistant/utils/host_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
// 国家数据模型
|
||||
// 国家数据模型(新增flag字段)
|
||||
class Country {
|
||||
final String countryId;
|
||||
final String name;
|
||||
final String code;
|
||||
final String? flag; // 新增国旗字段
|
||||
|
||||
Country({required this.countryId, required this.name, required this.code});
|
||||
Country({
|
||||
required this.countryId,
|
||||
required this.name,
|
||||
required this.code,
|
||||
this.flag, // 新增参数
|
||||
});
|
||||
|
||||
// 从JSON构建对象
|
||||
// 从JSON构建对象(添加flag字段解析)
|
||||
factory Country.fromJson(Map<String, dynamic> json) {
|
||||
return Country(
|
||||
countryId: json['country_id'],
|
||||
name: json['name'],
|
||||
code: json['code'],
|
||||
flag: json['flag'], // 解析国旗字段
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -83,14 +90,33 @@ class _CountryPageState extends State<CountryPage> {
|
||||
int _currentPage = 1;
|
||||
final int _pageSize = 20;
|
||||
bool _hasMoreData = true;
|
||||
late ScrollController _scrollController; // 优化滚动控制器
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_scrollController = ScrollController();
|
||||
_scrollController.addListener(_onScroll); // 注册滚动监听
|
||||
_fetchCountries();
|
||||
}
|
||||
|
||||
// 加载国家列表数据
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose(); // 释放资源
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// 滚动监听处理加载更多
|
||||
void _onScroll() {
|
||||
if (_isLoading) return;
|
||||
if (_hasMoreData &&
|
||||
_scrollController.position.pixels >=
|
||||
_scrollController.position.maxScrollExtent - 200) {
|
||||
_loadMore();
|
||||
}
|
||||
}
|
||||
|
||||
// 加载国家列表数据(使用POST请求)
|
||||
Future<void> _fetchCountries({bool isRefresh = false}) async {
|
||||
if (isRefresh) {
|
||||
setState(() {
|
||||
@@ -109,25 +135,27 @@ class _CountryPageState extends State<CountryPage> {
|
||||
final baseUrl = HostUtils().currentHost;
|
||||
final path = '/country/read';
|
||||
final url = '$baseUrl$path';
|
||||
// 打印请求基本信息
|
||||
debugPrint('====== 开始请求国家列表 ======');
|
||||
debugPrint('请求URL: $url');
|
||||
debugPrint('请求参数: page=$_currentPage, page_size=$_pageSize');
|
||||
|
||||
final dio = Dio();
|
||||
// 使用POST请求,通过data传递表单数据(适配后端form接收方式)
|
||||
final response = await dio.post(
|
||||
url,
|
||||
queryParameters: {
|
||||
data: {
|
||||
'page': _currentPage,
|
||||
'page_size': _pageSize,
|
||||
'name': '',
|
||||
'code': '',
|
||||
'country_id': '',
|
||||
'flag': '', // 新增国旗查询参数
|
||||
},
|
||||
options: Options(headers: {'Content-Type': 'application/json'}),
|
||||
options: Options(
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
),
|
||||
);
|
||||
|
||||
// 打印响应状态
|
||||
debugPrint('请求成功,状态码: ${response.statusCode}');
|
||||
debugPrint('响应数据: ${response.data}');
|
||||
|
||||
@@ -149,7 +177,6 @@ class _CountryPageState extends State<CountryPage> {
|
||||
_currentPage++;
|
||||
_errorMessage = null;
|
||||
});
|
||||
// 打印数据处理结果
|
||||
debugPrint('数据解析成功,当前列表总数: ${_countries.length}');
|
||||
debugPrint('是否还有更多数据: $_hasMoreData,下一页: $_currentPage');
|
||||
} else {
|
||||
@@ -232,7 +259,6 @@ class _CountryPageState extends State<CountryPage> {
|
||||
icon: const Icon(Icons.add),
|
||||
onPressed: () async {
|
||||
debugPrint('点击新增按钮,跳转到新增页面');
|
||||
// 跳转到新增页面,返回时刷新列表
|
||||
final result = await Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(builder: (context) => const AddCountryPage()),
|
||||
@@ -294,21 +320,12 @@ class _CountryPageState extends State<CountryPage> {
|
||||
);
|
||||
}
|
||||
},
|
||||
// 监听滚动加载更多
|
||||
controller: ScrollController()
|
||||
..addListener(() {
|
||||
if (_isLoading) return;
|
||||
if (_hasMoreData &&
|
||||
ScrollController().position.pixels >=
|
||||
ScrollController().position.maxScrollExtent - 200) {
|
||||
_loadMore();
|
||||
}
|
||||
}),
|
||||
controller: _scrollController, // 使用优化后的滚动控制器
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 构建国家列表项
|
||||
// 构建国家列表项(新增国旗显示)
|
||||
Widget _buildCountryItem(ThemeData theme, Country country) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -327,18 +344,27 @@ class _CountryPageState extends State<CountryPage> {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
// 国旗显示区域
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: theme.colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
image: country.flag != null && country.flag!.isNotEmpty
|
||||
? DecorationImage(
|
||||
image: NetworkImage(country.flag!),
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.flag,
|
||||
size: 24,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
child: country.flag == null || country.flag!.isEmpty
|
||||
? Icon(
|
||||
Icons.flag,
|
||||
size: 24,
|
||||
color: theme.colorScheme.secondary,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
Expanded(
|
||||
|
||||
@@ -19,6 +19,7 @@ type ReadRequest struct {
|
||||
CountryID string `form:"country_id"` // 国家ID,可选
|
||||
Name string `form:"name"` // 国家名称,可选
|
||||
Code string `form:"code"` // 国家代码,可选
|
||||
Flag string `form:"flag"` // 国旗信息,新增可选参数
|
||||
Page string `form:"page"` // 页码,可选
|
||||
PageSize string `form:"page_size"` // 每页条数,可选
|
||||
}
|
||||
@@ -31,11 +32,12 @@ type ReadData struct {
|
||||
Items []CountryInfoViewItem `json:"items"` // 数据列表
|
||||
}
|
||||
|
||||
// CountryInfoViewItem 视图数据项结构
|
||||
// CountryInfoViewItem 视图数据项结构,新增国旗字段
|
||||
type CountryInfoViewItem struct {
|
||||
CountryID string `json:"country_id"` // 国家ID
|
||||
Name string `json:"name"` // 国家名称
|
||||
Code string `json:"code"` // 国家代码
|
||||
Flag string `json:"flag"` // 国旗信息,新增字段
|
||||
}
|
||||
|
||||
// ReadResponse 读取响应结构
|
||||
@@ -76,19 +78,6 @@ func ReadHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 验证查询条件至少有一个不为空
|
||||
if req.CountryID == "" && req.Name == "" && req.Code == "" {
|
||||
zap.L().Warn("⚠️ 请求参数验证失败",
|
||||
zap.String("req_id", reqID),
|
||||
zap.String("reason", "country_id、name、code不能同时为空"),
|
||||
)
|
||||
c.JSON(http.StatusBadRequest, ReadResponse{
|
||||
Success: false,
|
||||
Message: "请求参数错误:country_id、name、code不能同时为空",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 处理分页参数默认值
|
||||
page, err := strconv.Atoi(req.Page)
|
||||
if err != nil || page < 1 {
|
||||
@@ -104,6 +93,7 @@ func ReadHandler(c *gin.Context) {
|
||||
zap.String("country_id", req.CountryID),
|
||||
zap.String("name", req.Name),
|
||||
zap.String("code", req.Code),
|
||||
zap.String("flag", req.Flag), // 新增国旗查询参数日志
|
||||
zap.Int("page", page),
|
||||
zap.Int("page_size", pageSize),
|
||||
)
|
||||
@@ -128,9 +118,15 @@ func ReadHandler(c *gin.Context) {
|
||||
args = append(args, "%"+req.Code+"%")
|
||||
paramIndex++
|
||||
}
|
||||
// 新增国旗查询条件
|
||||
if req.Flag != "" {
|
||||
whereClauses = append(whereClauses, "flag LIKE $"+strconv.Itoa(paramIndex))
|
||||
args = append(args, "%"+req.Flag+"%")
|
||||
paramIndex++
|
||||
}
|
||||
|
||||
// 构建基础SQL
|
||||
baseSQL := "SELECT country_id, name, code FROM country_info_view"
|
||||
// 构建基础SQL,新增flag字段查询
|
||||
baseSQL := "SELECT country_id, name, code, flag FROM country_info_view"
|
||||
countSQL := "SELECT COUNT(*) FROM country_info_view"
|
||||
if len(whereClauses) > 0 {
|
||||
whereStr := " WHERE " + strings.Join(whereClauses, " AND ")
|
||||
@@ -141,11 +137,11 @@ func ReadHandler(c *gin.Context) {
|
||||
// 计算分页偏移量
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 拼接分页SQL(使用fmt.Sprintf更清晰)
|
||||
// 拼接分页SQL
|
||||
querySQL := fmt.Sprintf("%s ORDER BY country_id LIMIT $%d OFFSET $%d", baseSQL, paramIndex, paramIndex+1)
|
||||
args = append(args, pageSize, offset)
|
||||
|
||||
// 查询总条数(修正参数传递方式)
|
||||
// 查询总条数
|
||||
var total int64
|
||||
countArgs := args[:len(args)-2] // 排除分页参数
|
||||
err = db.DB.QueryRow(countSQL, countArgs...).Scan(&total)
|
||||
@@ -176,11 +172,11 @@ func ReadHandler(c *gin.Context) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// 处理查询结果
|
||||
// 处理查询结果,新增flag字段扫描
|
||||
var items []CountryInfoViewItem
|
||||
for rows.Next() {
|
||||
var item CountryInfoViewItem
|
||||
if err := rows.Scan(&item.CountryID, &item.Name, &item.Code); err != nil {
|
||||
if err := rows.Scan(&item.CountryID, &item.Name, &item.Code, &item.Flag); err != nil {
|
||||
zap.L().Error("❌ 解析查询结果失败",
|
||||
zap.String("req_id", reqID),
|
||||
zap.Error(err),
|
||||
@@ -228,4 +224,4 @@ func ReadHandler(c *gin.Context) {
|
||||
Items: items,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user