191 lines
5.6 KiB
Dart
191 lines
5.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:uuid/uuid.dart';
|
|
|
|
class House {
|
|
final String id;
|
|
final String title;
|
|
final String? address;
|
|
final double? latitude;
|
|
final double? longitude;
|
|
final double? totalPrice;
|
|
final double? totalPing;
|
|
final double? age;
|
|
final String? propertyType;
|
|
final String? summaryNotes;
|
|
final double? overallRating;
|
|
final bool isFavorite;
|
|
final DateTime? createdAt;
|
|
|
|
House({
|
|
required this.id,
|
|
required this.title,
|
|
this.address,
|
|
this.latitude,
|
|
this.longitude,
|
|
this.totalPrice,
|
|
this.totalPing,
|
|
this.age,
|
|
this.propertyType,
|
|
this.summaryNotes,
|
|
this.overallRating,
|
|
this.isFavorite = false,
|
|
this.createdAt,
|
|
});
|
|
|
|
factory House.fromJson(Map<String, dynamic> json) {
|
|
return House(
|
|
id: json['id'] as String? ?? const Uuid().v4(),
|
|
title: json['title'] as String? ?? '未命名物件',
|
|
address: json['address'] as String?,
|
|
latitude: double.tryParse(json['latitude']?.toString() ?? ''),
|
|
longitude: double.tryParse(json['longitude']?.toString() ?? ''),
|
|
totalPrice: double.tryParse(json['total_price']?.toString() ?? ''),
|
|
totalPing: double.tryParse(json['total_ping']?.toString() ?? ''),
|
|
age: double.tryParse(json['age']?.toString() ?? ''),
|
|
propertyType: json['property_type'] as String?,
|
|
summaryNotes: json['summary_notes'] as String?,
|
|
overallRating: double.tryParse(json['overall_rating']?.toString() ?? '0'),
|
|
isFavorite: json['is_favorite'] == 1 || json['is_favorite'] == true,
|
|
createdAt: json['created_at'] != null ? DateTime.tryParse(json['created_at']) : null,
|
|
);
|
|
}
|
|
|
|
// UI Helper: 評分顏色
|
|
Color get ratingColor {
|
|
if ((overallRating ?? 0) >= 4) return Colors.green;
|
|
if ((overallRating ?? 0) >= 3) return Colors.orange;
|
|
return Colors.red;
|
|
}
|
|
|
|
// 格式化顯示價格與坪數
|
|
String get priceTag => totalPrice != null ? '${totalPrice} 萬' : '價格未定';
|
|
String get infoSummary => '${propertyType ?? "未知"} | ${age ?? "?"}年 | ${totalPing ?? "?"}坪';
|
|
}
|
|
|
|
// 檢查項目定義 (Template)
|
|
class CheckItem {
|
|
final String id;
|
|
final String categoryId;
|
|
final String name;
|
|
final bool isDefault;
|
|
|
|
CheckItem({required this.id, required this.categoryId, required this.name, this.isDefault = true});
|
|
|
|
factory CheckItem.fromJson(Map<String, dynamic> json) {
|
|
return CheckItem(
|
|
id: json['id'] as String,
|
|
categoryId: json['category_id'] as String,
|
|
name: json['name'] as String,
|
|
isDefault: json['is_default'] == 1,
|
|
);
|
|
}
|
|
}
|
|
|
|
// 新增照片模型
|
|
class HousePhoto {
|
|
final String id;
|
|
final String houseId;
|
|
final String? evaluationId; // 關聯特定評估項目
|
|
final String filePath;
|
|
final String? description;
|
|
|
|
HousePhoto({required this.id, required this.houseId, this.evaluationId, required this.filePath, this.description});
|
|
|
|
factory HousePhoto.fromJson(Map<String, dynamic> json) {
|
|
return HousePhoto(
|
|
id: json['id'] as String,
|
|
houseId: json['house_id'] as String,
|
|
evaluationId: json['evaluation_id'] as String?,
|
|
filePath: json['file_path'] as String,
|
|
description: json['description'] as String?,
|
|
);
|
|
}
|
|
|
|
// 取得完整圖片網址
|
|
// String get fullUrl => "https://api.gex.com.tw/uploads/$filePath";
|
|
String get fullUrl {
|
|
if (filePath.startsWith('http')) {
|
|
return filePath;
|
|
}
|
|
return "https://api.gex.com.tw/uploads/$filePath";
|
|
}
|
|
}
|
|
|
|
// 新增評估紀錄模型
|
|
class HouseEvaluation {
|
|
String? id; // 【修改】允許為 null (新建立的評估還沒有 ID)
|
|
String houseId; // 移除 final
|
|
String checkItemId; // 移除 final
|
|
String itemName; // 移除 final,讓前端可以賦值
|
|
int score; // 【修改】移除 final,讓表單可以更新分數
|
|
String? itemMemo; // 【修改】移除 final,讓表單可以更新備註
|
|
|
|
HouseEvaluation({
|
|
this.id, // 【修改】移除 required
|
|
required this.houseId,
|
|
required this.checkItemId,
|
|
required this.itemName, // 給予預設值,解決報錯
|
|
this.score = 0,
|
|
this.itemMemo,
|
|
});
|
|
|
|
factory HouseEvaluation.fromJson(Map<String, dynamic> json) {
|
|
return HouseEvaluation(
|
|
id: json['id'] as String?,
|
|
houseId: json['house_id'] as String? ?? '',
|
|
checkItemId: json['check_item_id'] as String? ?? '',
|
|
itemName: json['itemName'] as String? ?? '',
|
|
score: int.tryParse(json['score']?.toString() ?? '0') ?? 0,
|
|
itemMemo: json['item_memo'] as String?,
|
|
);
|
|
}
|
|
}
|
|
|
|
// Tag
|
|
class Tag {
|
|
final String id;
|
|
final String name;
|
|
final String colorHex;
|
|
bool isSelected; // UI 用:判斷是否被選取
|
|
|
|
Tag({
|
|
required this.id,
|
|
required this.name,
|
|
required this.colorHex,
|
|
this.isSelected = false,
|
|
});
|
|
|
|
factory Tag.fromJson(Map<String, dynamic> json) {
|
|
return Tag(
|
|
id: json['id'] as String,
|
|
name: json['name'] as String,
|
|
colorHex: json['color_hex'] as String? ?? '#CCCCCC',
|
|
);
|
|
}
|
|
|
|
// 輔助:轉換 Hex String 為 Color 物件
|
|
Color get color {
|
|
final hexCode = colorHex.replaceAll('#', '');
|
|
return Color(int.parse('FF$hexCode', radix: 16));
|
|
}
|
|
}
|
|
|
|
// 檢查項目大類模型
|
|
class CheckCategory {
|
|
final String id;
|
|
final String name;
|
|
final int sortOrder;
|
|
|
|
CheckCategory({required this.id, required this.name, required this.sortOrder});
|
|
|
|
factory CheckCategory.fromJson(Map<String, dynamic> json) {
|
|
return CheckCategory(
|
|
id: json['id'] as String,
|
|
name: json['name'] as String,
|
|
sortOrder: int.tryParse(json['sort_order']?.toString() ?? '0') ?? 0,
|
|
);
|
|
}
|
|
}
|
|
|