出行方式(飞机/火车/游船/自驾/地铁…)原本在路线地图标记、节点卡片交通行、
节点详情「出行方式」行一律显示节点类型图标(赶路节点恒为 🚗),易让人误解。
- TravelModeController 新增 MODE_ICONS(mode_type → emoji 映射)
- ActivityController::show() 两路查询 LEFT JOIN travel_modes 取 mode_type,注入 modeIcons
- 路线地图标记 / 卡片交通行 / 详情出行方式行:优先用出行方式图标,
未选出行方式时回退节点类型图标
250 lines
7.8 KiB
PHP
250 lines
7.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Auth;
|
|
use App\Database;
|
|
|
|
class TravelModeController
|
|
{
|
|
public const MODE_LABELS = [
|
|
'drive_own' => '自驾(自有车)',
|
|
'drive_rental' => '自驾(租车)',
|
|
'train' => '火车/高铁',
|
|
'plane' => '飞机',
|
|
'bus' => '大巴/公交',
|
|
'subway' => '地铁',
|
|
'taxi' => '出租车/网约车',
|
|
'bike' => '骑行',
|
|
'walk' => '步行',
|
|
'ship' => '轮渡/游船',
|
|
'other' => '其他',
|
|
];
|
|
|
|
/**
|
|
* 出行方式 → 地图/卡片上的图标(emoji)。
|
|
* 自驾两类仍用 🚗(本来就是开车);其余按交通工具区分,
|
|
* 避免路线地图上「一律汽车」让人误解(批 19)。未列入的 mode_type 回退到节点类型图标。
|
|
*/
|
|
public const MODE_ICONS = [
|
|
'drive_own' => '🚗',
|
|
'drive_rental' => '🚙',
|
|
'train' => '🚄',
|
|
'plane' => '✈️',
|
|
'bus' => '🚌',
|
|
'subway' => '🚇',
|
|
'taxi' => '🚕',
|
|
'bike' => '🚲',
|
|
'walk' => '🚶',
|
|
'ship' => '🚢',
|
|
'other' => '🔀',
|
|
];
|
|
|
|
public function index(): void
|
|
{
|
|
$this->requireLogin();
|
|
|
|
$db = Database::get();
|
|
$familyId = Auth::familyId();
|
|
|
|
$stmt = $db->prepare('
|
|
SELECT tm.*, v.plate_number, v.model_name
|
|
FROM travel_modes tm
|
|
LEFT JOIN vehicles v ON v.id = tm.vehicle_id
|
|
WHERE tm.family_id = ?
|
|
ORDER BY FIELD(tm.mode_type, "drive_own","drive_rental","train","plane","bus","subway","taxi","bike","walk","ship","other"), tm.id ASC
|
|
');
|
|
$stmt->execute([$familyId]);
|
|
$modes = $stmt->fetchAll();
|
|
|
|
foreach ($modes as &$m) {
|
|
$s = $db->prepare('SELECT COUNT(*) FROM trip_nodes WHERE travel_mode_id = ?');
|
|
$s->execute([$m['id']]);
|
|
$m['node_count'] = (int)$s->fetchColumn();
|
|
}
|
|
unset($m);
|
|
|
|
view('travel_modes/index', [
|
|
'modes' => $modes,
|
|
'modeLabels' => self::MODE_LABELS,
|
|
'pageTitle' => '出行方式管理',
|
|
]);
|
|
}
|
|
|
|
public function create(): void
|
|
{
|
|
$this->requireLogin();
|
|
view('travel_modes/form', [
|
|
'mode' => null,
|
|
'vehicles' => $this->fetchVehicles(),
|
|
'modeLabels' => self::MODE_LABELS,
|
|
'pageTitle' => '新增出行方式',
|
|
]);
|
|
}
|
|
|
|
public function store(): void
|
|
{
|
|
$this->requireLogin();
|
|
csrf_check();
|
|
|
|
$db = Database::get();
|
|
$familyId = Auth::familyId();
|
|
|
|
$modeType = $_POST['mode_type'] ?? '';
|
|
if (!isset(self::MODE_LABELS[$modeType])) {
|
|
flash('error', '无效的交通类型');
|
|
redirect('travel_modes/create');
|
|
}
|
|
|
|
$vehicleId = $this->validateVehicle($modeType, $db, $familyId, 'travel_modes/create');
|
|
|
|
$stmt = $db->prepare('INSERT INTO travel_modes (family_id, mode_type, vehicle_id, notes) VALUES (?, ?, ?, ?)');
|
|
$stmt->execute([
|
|
$familyId,
|
|
$modeType,
|
|
$vehicleId,
|
|
$this->strOrNull('notes'),
|
|
]);
|
|
|
|
flash('error', '创建成功');
|
|
redirect('travel_modes/index');
|
|
}
|
|
|
|
public function edit(): void
|
|
{
|
|
$this->requireLogin();
|
|
$id = (int)($_GET['id'] ?? 0);
|
|
$db = Database::get();
|
|
|
|
$stmt = $db->prepare('SELECT * FROM travel_modes WHERE id = ? AND family_id = ?');
|
|
$stmt->execute([$id, Auth::familyId()]);
|
|
$mode = $stmt->fetch();
|
|
if (!$mode) {
|
|
http_response_code(404);
|
|
exit('出行方式不存在');
|
|
}
|
|
|
|
view('travel_modes/form', [
|
|
'mode' => $mode,
|
|
'vehicles' => $this->fetchVehicles(),
|
|
'modeLabels' => self::MODE_LABELS,
|
|
'pageTitle' => '编辑出行方式',
|
|
]);
|
|
}
|
|
|
|
public function update(): void
|
|
{
|
|
$this->requireLogin();
|
|
csrf_check();
|
|
|
|
$db = Database::get();
|
|
$familyId = Auth::familyId();
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
|
|
$stmt = $db->prepare('SELECT * FROM travel_modes WHERE id = ? AND family_id = ?');
|
|
$stmt->execute([$id, $familyId]);
|
|
if (!$stmt->fetch()) {
|
|
http_response_code(404);
|
|
exit('出行方式不存在');
|
|
}
|
|
|
|
$modeType = $_POST['mode_type'] ?? '';
|
|
if (!isset(self::MODE_LABELS[$modeType])) {
|
|
flash('error', '无效的交通类型');
|
|
redirect('travel_modes/edit', ['id' => $id]);
|
|
}
|
|
|
|
$vehicleId = $this->validateVehicle($modeType, $db, $familyId, 'travel_modes/edit', ['id' => $id]);
|
|
|
|
$stmt = $db->prepare('UPDATE travel_modes SET mode_type = ?, vehicle_id = ?, notes = ? WHERE id = ?');
|
|
$stmt->execute([
|
|
$modeType,
|
|
$vehicleId,
|
|
$this->strOrNull('notes'),
|
|
$id,
|
|
]);
|
|
|
|
flash('error', '保存成功');
|
|
redirect('travel_modes/index');
|
|
}
|
|
|
|
public function destroy(): void
|
|
{
|
|
$this->requireLogin();
|
|
csrf_check();
|
|
|
|
$db = Database::get();
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
|
|
$stmt = $db->prepare('SELECT * FROM travel_modes WHERE id = ? AND family_id = ?');
|
|
$stmt->execute([$id, Auth::familyId()]);
|
|
if (!$stmt->fetch()) {
|
|
flash('error', '出行方式不存在');
|
|
redirect('travel_modes/index');
|
|
}
|
|
|
|
$cnt = $db->prepare('SELECT COUNT(*) FROM trip_nodes WHERE travel_mode_id = ?');
|
|
$cnt->execute([$id]);
|
|
$usedCount = (int)$cnt->fetchColumn();
|
|
if ($usedCount > 0) {
|
|
flash('error', "该出行方式已被 {$usedCount} 个节点使用,无法删除");
|
|
redirect('travel_modes/index');
|
|
}
|
|
|
|
$stmt = $db->prepare('DELETE FROM travel_modes WHERE id = ?');
|
|
$stmt->execute([$id]);
|
|
|
|
flash('error', '删除成功');
|
|
redirect('travel_modes/index');
|
|
}
|
|
|
|
private function validateVehicle(string $modeType, \PDO $db, int $familyId, string $route, array $params = []): ?int
|
|
{
|
|
if (!in_array($modeType, ['drive_own', 'drive_rental'], true)) {
|
|
return null;
|
|
}
|
|
|
|
$vehicleId = (int)($_POST['vehicle_id'] ?? 0);
|
|
if ($vehicleId <= 0) {
|
|
flash('error', '自驾类型必须选择车辆');
|
|
redirect($route, $params);
|
|
}
|
|
$chk = $db->prepare('SELECT id, vehicle_type FROM vehicles WHERE id = ? AND family_id = ?');
|
|
$chk->execute([$vehicleId, $familyId]);
|
|
$veh = $chk->fetch();
|
|
if (!$veh) {
|
|
flash('error', '车辆不存在');
|
|
redirect($route, $params);
|
|
}
|
|
if ($modeType === 'drive_own' && $veh['vehicle_type'] !== 'own') {
|
|
flash('error', '自有车类型必须选择自有车辆');
|
|
redirect($route, $params);
|
|
}
|
|
if ($modeType === 'drive_rental' && $veh['vehicle_type'] !== 'rental') {
|
|
flash('error', '租车类型必须选择租用车辆');
|
|
redirect($route, $params);
|
|
}
|
|
return $vehicleId;
|
|
}
|
|
|
|
private function fetchVehicles(): array
|
|
{
|
|
$stmt = Database::get()->prepare('SELECT * FROM vehicles WHERE family_id = ? ORDER BY vehicle_type ASC, is_default DESC, id ASC');
|
|
$stmt->execute([Auth::familyId()]);
|
|
return $stmt->fetchAll();
|
|
}
|
|
|
|
private function strOrNull(string $key): ?string
|
|
{
|
|
$v = trim($_POST[$key] ?? '');
|
|
return $v === '' ? null : $v;
|
|
}
|
|
|
|
private function requireLogin(): void
|
|
{
|
|
if (!Auth::check()) {
|
|
redirect('auth/login');
|
|
}
|
|
}
|
|
} |