HEX
Server: Apache/2.4.37 (CentOS Stream) OpenSSL/1.1.1k
System: Linux ysnet.com.tw 4.18.0-553.5.1.el8.x86_64 #1 SMP Tue May 21 05:46:01 UTC 2024 x86_64
User: test (521)
PHP: 7.4.33
Disabled: NONE
Upload Files
File: /var/www/test/Installationlist/repair.php
<?php
// ============================================================
// repair.php  ─  電子派工維修單
// ============================================================
ob_start();
session_start();

// ── 第一次帶參數進來,存 Session 後導向乾淨 URL ─────────────
if (isset($_GET['user_id'])) {
    $fields = ['user_id','member','user_name','memberaddress','email','homenumber',
               'phone','pppoeid','pppoepw','ipaddress','basenumber','speed',
               'homene','inforne','networkcm','question','ENname','APname',
               'ENphone'];
    foreach ($fields as $f) {
        $_SESSION['rp_'.$f] = trim($_GET[$f] ?? '');
    }
    header('Location: repair.php');
    exit;
}

// ── 從 Session 讀取 ─────────────────────────────────────────
$G = [];
$fields = ['user_id','member','user_name','memberaddress','email','homenumber',
           'phone','pppoeid','pppoepw','ipaddress','basenumber','speed',
           'homene','inforne','networkcm','question','ENname','APname',
           'ENphone'];
foreach ($fields as $f) $G[$f] = $_SESSION['rp_'.$f] ?? '';

if (empty($G['user_id'])) {
    die('<p style="color:red;font-size:18px;padding:20px">請透過正確連結開啟此頁面</p>');
}

$todayStr = (new DateTime())->format('Y-m-d');
$todayFmt = (new DateTime())->format('Y年m月d日');
?>
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>亞訊寬頻 電子派工維修單</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@3.19.0/dist/tabler-icons.min.css">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
:root{
    --green:#1D9E75;--green-lt:#E1F5EE;--green-dk:#0F6E56;--green-deep:#085041;
    --amber:#FAEEDA;--amber-txt:#633806;
    --red:#E24B4A;--red-lt:#FCEBEB;
    --gray-bg:#F2F2F7;--gray-bd:rgba(0,0,0,.11);--gray-bd2:rgba(0,0,0,.22);
    --text:#111;--text-sec:#555;--text-tert:#999;
    --radius-sm:8px;--radius-md:10px;--radius-lg:14px;
}
body{font-family:"Microsoft JhengHei","PingFang TC",sans-serif;font-size:15px;background:var(--gray-bg);color:var(--text);padding-bottom:100px}

/* topbar */
.topbar{position:sticky;top:0;z-index:200;background:#fff;border-bottom:0.5px solid var(--gray-bd);padding:10px 16px;display:flex;align-items:center;gap:10px}
.topbar-logo{font-size:17px;font-weight:700;color:var(--green-deep);letter-spacing:.5px}
.topbar-title{flex:1;text-align:center;font-size:15px;font-weight:500}
.topbar-date{font-size:11px;color:var(--text-sec);text-align:right;line-height:1.5}

/* content */
.content{padding:12px 14px;display:flex;flex-direction:column;gap:10px}

/* card */
.card{background:#fff;border-radius:var(--radius-lg);border:0.5px solid var(--gray-bd);overflow:hidden}
.card-hdr{display:flex;align-items:center;gap:8px;padding:9px 14px;border-bottom:0.5px solid var(--gray-bd);background:var(--gray-bg)}
.card-hdr i{font-size:15px;color:var(--green)}
.card-hdr-txt{font-size:13px;font-weight:600;flex:1}
.card-hdr-badge{font-size:11px;padding:2px 8px;border-radius:20px;background:var(--green-lt);color:var(--green-deep)}

/* info rows */
.irow{display:flex;align-items:stretch;border-bottom:0.5px solid var(--gray-bd)}
.irow:last-child{border-bottom:none}
.ilbl{width:78px;flex-shrink:0;font-size:11.5px;color:var(--text-sec);padding:8px 6px 8px 13px;background:var(--gray-bg);display:flex;align-items:center;justify-content:center;text-align:center;line-height:1.4}
.ival{flex:1;padding:8px 13px;font-size:13.5px;word-break:break-all;display:flex;align-items:center}
.ival.mono{font-family:monospace;font-size:12.5px;color:var(--text-sec)}
.ival.green{color:var(--green-dk);font-weight:500}
.ival.pre{white-space:pre-wrap;line-height:1.7;align-items:flex-start;padding-top:10px;padding-bottom:10px}
.ival.empty{color:var(--text-tert);font-style:italic}

/* chip tags */
.chips{display:flex;flex-wrap:wrap;gap:4px;padding:8px 13px}
.chip{display:inline-flex;align-items:center;gap:3px;font-size:12px;background:var(--green-lt);color:var(--green-deep);padding:3px 10px;border-radius:20px}
.chip.gray{background:var(--gray-bg);color:var(--text-sec)}

/* 工程師填寫區 */
.eng-textarea{
    width:100%;min-height:120px;font-size:14px;line-height:1.8;
    padding:11px 13px;border:none;outline:none;resize:vertical;
    font-family:inherit;color:var(--text);background:#fff;
    border-top:0.5px solid var(--gray-bd)
}
.eng-textarea:focus{background:#FFFDF9}
.char-count{font-size:11px;color:var(--text-tert);text-align:right;padding:4px 13px 8px}

/* 使用設備:多層選單卡片 */
.device-card{border:0.5px solid var(--gray-bd2);border-radius:var(--radius-md);margin:8px 13px;overflow:hidden;background:#fafafa}
.device-row{display:flex;align-items:center;gap:7px;padding:8px 10px;border-bottom:0.5px solid var(--gray-bd)}
.device-row:last-child{border-bottom:none}
.device-row.sub{background:#fff}
.dev-sel{flex:1 1 0;min-width:0;font-size:13px;padding:7px 6px;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-sm);background:#fff;color:var(--text);font-family:inherit}
.sn-wrap{display:flex;align-items:center;gap:5px;flex:1 1 0;min-width:0}
.sn-inp{flex:1;min-width:0;font-size:13px;padding:7px 8px;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-sm);background:#fff;color:var(--text);font-family:monospace}
.basenum-inp{flex:1 1 0;min-width:0;font-size:13px;padding:7px 8px;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-sm);background:#fff;color:var(--text);font-family:monospace}
.rm-btn{width:30px;height:30px;border:none;border-radius:50%;background:var(--gray-bg);display:flex;align-items:center;justify-content:center;cursor:pointer;flex-shrink:0;color:var(--text-sec)}
.add-row-btn{display:flex;align-items:center;gap:6px;padding:9px 13px;font-size:13px;color:var(--green-dk);cursor:pointer;border-top:0.5px solid var(--gray-bd);background:none;border-left:none;border-right:none;border-bottom:none;width:100%;font-family:inherit}

/* 收費 */
.fee-toggle{display:flex;gap:0;padding:10px 13px}
.fee-btn{flex:1;padding:9px;text-align:center;font-size:14px;border:0.5px solid var(--gray-bd2);cursor:pointer;font-family:inherit;transition:all .15s;background:#fff;color:var(--text-sec)}
.fee-btn:first-child{border-radius:var(--radius-sm) 0 0 var(--radius-sm)}
.fee-btn:last-child{border-radius:0 var(--radius-sm) var(--radius-sm) 0;border-left:none}
.fee-btn.sel-free{background:var(--green-lt);color:var(--green-dk);font-weight:500;border-color:var(--green)}
.fee-btn.sel-paid{background:#FFF3F3;color:var(--red);font-weight:500;border-color:var(--red)}

.fee-item-row{display:flex;align-items:center;gap:7px;padding:8px 13px;border-bottom:0.5px solid var(--gray-bd)}
.fee-item-row:last-child{border-bottom:none}
.fee-type-sel{flex:1;font-size:13px;padding:7px 8px;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-sm);background:#fff;color:var(--text);font-family:inherit}
.fee-amt-inp{width:90px;font-size:14px;padding:7px 8px;text-align:right;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-sm);background:#fff;color:var(--text);font-family:inherit;flex-shrink:0}
.fee-total-row{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-top:0.5px solid var(--gray-bd)}
.fee-total-lbl{font-size:13px;color:var(--text-sec)}
.fee-total-amt{font-size:18px;font-weight:600;color:var(--red)}
.deposit-row{display:flex;justify-content:space-between;align-items:center;padding:8px 14px;border-top:0.5px solid var(--gray-bd)}
.deposit-lbl{font-size:13px;color:var(--text-sec)}
.deposit-amt{font-size:15px;font-weight:500;color:var(--amber-txt)}

/* 確認勾選 */
.chk-item{display:flex;align-items:center;gap:10px;padding:11px 13px;border-bottom:0.5px solid var(--gray-bd);cursor:pointer;user-select:none}
.chk-item:last-child{border-bottom:none}
.chk-box{width:24px;height:24px;border-radius:6px;border:1.5px solid var(--gray-bd2);display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .15s;color:transparent}
.chk-box.on{background:var(--green);border-color:var(--green);color:#fff}
.chk-txt{font-size:14px}
.chk-sub{font-size:11px;color:var(--text-sec);margin-top:2px}

/* 簽名 */
.sig-trigger{border:1.5px dashed var(--gray-bd2);border-radius:var(--radius-md);background:var(--gray-bg);height:76px;display:flex;align-items:center;justify-content:center;cursor:pointer;gap:8px;color:var(--text-sec);font-size:13px;margin:11px 13px}
#sig-preview-wrap,#esig-preview-wrap{margin:11px 13px;display:none}
.sig-preview-img{width:100%;border:0.5px solid var(--gray-bd);border-radius:var(--radius-md);background:#fff;display:block}
.sig-rebtn{width:100%;margin-top:6px;padding:7px;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-md);background:none;cursor:pointer;font-size:12px;color:var(--text-sec);font-family:inherit}

/* 簽名 Modal */
.sig-modal{display:none;position:fixed;inset:0;z-index:9999;background:#fff;flex-direction:column}
.sig-modal-bar{display:flex;align-items:center;justify-content:space-between;padding:11px 16px;border-bottom:0.5px solid var(--gray-bd);flex-shrink:0}
.sig-modal-title{font-size:16px;font-weight:500}
.sig-clr-btn{padding:7px 14px;border:0.5px solid var(--gray-bd2);border-radius:var(--radius-sm);background:none;font-size:13px;cursor:pointer;color:var(--text-sec);font-family:inherit}
.sig-ok-btn{padding:7px 18px;background:var(--green);color:#fff;border:none;border-radius:var(--radius-sm);font-size:14px;font-weight:500;cursor:pointer;font-family:inherit}
.sig-modal-area{flex:1;position:relative;overflow:hidden;background:#fafafa}
.sig-canvas-el{position:absolute;inset:0;touch-action:none;cursor:crosshair;display:block}
.sig-hint{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;pointer-events:none;color:var(--text-tert)}
.sig-modal-tip{padding:7px 16px;text-align:center;font-size:11px;color:var(--text-tert);border-top:0.5px solid var(--gray-bd);flex-shrink:0}

/* 底部 */
.bottom-bar{position:sticky;bottom:0;background:#fff;border-top:0.5px solid var(--gray-bd);padding:11px 14px}
.submit-btn{width:100%;padding:14px;background:var(--green);color:#fff;border:none;border-radius:var(--radius-md);font-size:15px;font-weight:500;cursor:pointer;font-family:inherit;display:flex;align-items:center;justify-content:center;gap:6px}
.submit-btn:disabled{opacity:.35;cursor:not-allowed}

/* 遮罩 */
#submitting-overlay{display:none;position:fixed;inset:0;background:rgba(255,255,255,.96);z-index:9998;flex-direction:column;align-items:center;justify-content:center;gap:16px}
</style>
</head>
<body>

<div class="topbar">
    <div class="topbar-logo">YSNET</div>
    <div class="topbar-title">電子派工維修單</div>
    <div class="topbar-date"><?= $todayFmt ?></div>
</div>

<div class="content">

<!-- ── 用戶基本資料 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-user"></i><span class="card-hdr-txt">用戶基本資料</span></div>
    <div class="irow"><div class="ilbl">用戶編號</div><div class="ival mono"><?= htmlspecialchars($G['user_id']) ?></div></div>
    <div class="irow"><div class="ilbl">姓名</div><div class="ival"><?= htmlspecialchars($G['user_name']) ?></div></div>
    <div class="irow"><div class="ilbl">居住社區</div><div class="ival"><?= htmlspecialchars($G['member']) ?></div></div>
    <div class="irow"><div class="ilbl">裝機地址</div><div class="ival"><?= htmlspecialchars($G['memberaddress']) ?: '<span class="ival empty">—</span>' ?></div></div>
    <div class="irow"><div class="ilbl">住家電話</div><div class="ival"><?= htmlspecialchars($G['homenumber']) ?: '<span class="ival empty">—</span>' ?></div></div>
    <div class="irow"><div class="ilbl">行動電話</div><div class="ival"><?= htmlspecialchars($G['phone']) ?></div></div>
    <div class="irow"><div class="ilbl">電子郵件</div><div class="ival" style="font-size:12.5px"><?= htmlspecialchars($G['email']) ?></div></div>
</div>

<!-- ── 網路資訊 ── -->
<?php $isStaticIP = !empty($G['ipaddress']); ?>
<div class="card">
    <div class="card-hdr">
        <i class="ti ti-wifi"></i>
        <span class="card-hdr-txt">網路資訊</span>
        <span style="margin-left:auto;font-size:11px;padding:2px 8px;border-radius:20px;
            background:<?= $isStaticIP ? '#EEF2FF' : 'var(--green-lt)' ?>;
            color:<?= $isStaticIP ? '#3730A3' : 'var(--green-deep)' ?>">
            <?= $isStaticIP ? '靜態 IP 上網' : 'PPPoE 撥接上網' ?>
        </span>
    </div>
    <?php if ($isStaticIP): ?>
        <!-- 靜態 IP:顯示 IP 位址,不顯示 PPPoE -->
        <div class="irow">
            <div class="ilbl">IP 位址</div>
            <div class="ival mono" style="font-size:15px;font-weight:500;color:#3730A3"><?= htmlspecialchars($G['ipaddress']) ?></div>
        </div>
    <?php else: ?>
        <!-- PPPoE:顯示帳號密碼,不顯示 IP -->
        <div class="irow">
            <div class="ilbl">PPPoE 帳號</div>
            <div class="ival mono"><?= htmlspecialchars($G['pppoeid']) ?></div>
        </div>
        <div class="irow">
            <div class="ilbl">PPPoE 密碼</div>
            <div class="ival mono"><?= htmlspecialchars($G['pppoepw']) ?></div>
        </div>
    <?php endif; ?>
    <div class="irow"><div class="ilbl">據點編號</div><div class="ival mono"><?= htmlspecialchars($G['basenumber']) ?></div></div>
    <div class="irow"><div class="ilbl">使用方案</div><div class="ival green"><?= htmlspecialchars($G['speed']) ?></div></div>
</div>

<!-- ── 設備資訊 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-server"></i><span class="card-hdr-txt">設備資訊</span></div>
    <div class="irow">
        <div class="ilbl">家中設備</div>
        <div class="ival"><?= $G['homene'] ? htmlspecialchars($G['homene']) : '<span style="color:var(--text-tert)">—</span>' ?></div>
    </div>
    <div class="irow">
        <div class="ilbl">資訊箱設備</div>
        <div class="ival"><?= $G['inforne'] ? htmlspecialchars($G['inforne']) : '<span style="color:var(--text-tert)">—</span>' ?></div>
    </div>
    <div class="irow">
        <div class="ilbl">施工方式</div>
        <div class="ival"><?= $G['networkcm'] ? htmlspecialchars($G['networkcm']) : '<span style="color:var(--text-tert)">—</span>' ?></div>
    </div>
</div>

<!-- ── 問題狀況描述 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-alert-circle"></i><span class="card-hdr-txt">問題狀況描述</span></div>
    <div class="ival pre" style="padding:11px 13px;min-height:60px;font-size:14px"><?php
        echo $G['question'] ? htmlspecialchars($G['question']) : '<span style="color:var(--text-tert);font-style:italic">(無描述)</span>';
    ?></div>
</div>

<!-- ── 工程師維修概況紀錄 ── -->
<div class="card">
    <div class="card-hdr">
        <i class="ti ti-pencil"></i>
        <span class="card-hdr-txt">維修概況紀錄</span>
        <span style="color:var(--red);font-size:12px;margin-left:4px">*必填</span>
    </div>
    <textarea id="repair-content" class="eng-textarea"
        placeholder="請填寫本次維修的處理內容、更換零件、調整項目、測試結果…"
        maxlength="1000" oninput="onRepairInput()"></textarea>
    <div class="char-count"><span id="char-num">0</span> / 1000</div>
</div>

<!-- ── 使用設備 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-package"></i><span class="card-hdr-txt">使用設備</span></div>
    <div id="device-container"></div>
    <button type="button" class="add-row-btn" onclick="addDeviceEntry()">
        <i class="ti ti-plus" style="font-size:14px"></i> 新增設備紀錄
    </button>
</div>

<!-- ── 收費紀錄 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-receipt"></i><span class="card-hdr-txt">收費紀錄</span></div>
    <div class="fee-toggle">
        <button type="button" class="fee-btn" id="btn-free" onclick="setFeeMode('free')">本次無收費</button>
        <button type="button" class="fee-btn" id="btn-paid" onclick="setFeeMode('paid')">本次收費</button>
    </div>
    <div id="fee-section" style="display:none">
        <div id="fee-container"></div>
        <button type="button" class="add-row-btn" onclick="addFeeRow()">
            <i class="ti ti-plus" style="font-size:14px"></i> 新增收費項目
        </button>
        <div class="fee-total-row">
            <span class="fee-total-lbl">本次收費總計</span>
            <span class="fee-total-amt" id="fee-total">$0</span>
        </div>
        <div class="deposit-row" id="deposit-row" style="display:none">
            <span class="deposit-lbl">押金</span>
            <span class="deposit-amt" id="deposit-total">$0</span>
        </div>
    </div>
</div>

<!-- ── 確認事項 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-list-check"></i><span class="card-hdr-txt">確認事項</span></div>
    <div class="chk-item" onclick="toggleChk(this)">
        <div class="chk-box"><i class="ti ti-check" style="font-size:14px"></i></div>
        <div><div class="chk-txt">維修概況紀錄已填寫完整</div><div class="chk-sub">本次處理項目無誤</div></div>
    </div>
    <div class="chk-item" onclick="toggleChk(this)">
        <div class="chk-box"><i class="ti ti-check" style="font-size:14px"></i></div>
        <div><div class="chk-txt">已向客戶說明維修結果及費用</div><div class="chk-sub">客戶已了解本次服務內容</div></div>
    </div>
    <div class="chk-item" onclick="toggleChk(this)">
        <div class="chk-box"><i class="ti ti-check" style="font-size:14px"></i></div>
        <div><div class="chk-txt">網路連線已確認正常</div><div class="chk-sub">現場測試完成</div></div>
    </div>
</div>

<!-- ── 客戶簽名 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-signature"></i><span class="card-hdr-txt">客戶簽名</span>
        <span id="csig-badge" style="margin-left:auto;font-size:11px;padding:2px 8px;border-radius:20px;background:var(--amber);color:var(--amber-txt)">未簽名</span>
    </div>
    <div id="sig-trigger" onclick="openSigModal('c')" class="sig-trigger">
        <i class="ti ti-pencil" style="font-size:20px;color:var(--green)"></i>
        點此開啟客戶簽名板
    </div>
    <div id="sig-preview-wrap">
        <img id="sig-preview-img" class="sig-preview-img">
        <button class="sig-rebtn" onclick="openSigModal('c')"><i class="ti ti-pencil" style="font-size:12px"></i> 重新簽名</button>
    </div>
</div>

<!-- ── 工程師確認 ── -->
<div class="card">
    <div class="card-hdr"><i class="ti ti-user-check"></i><span class="card-hdr-txt">工程師確認</span></div>
    <div class="chk-item" id="eng-chk-item" onclick="toggleEngChk()">
        <div class="chk-box" id="eng-chk-box"><i class="ti ti-check" style="font-size:14px"></i></div>
        <div>
            <div class="chk-txt">本表單資料確認無誤,維修工作已完成</div>
        </div>
    </div>
</div>

</div><!-- /content -->

<!-- 送出遮罩 -->
<div id="submitting-overlay" style="display:none;position:fixed;inset:0;background:rgba(255,255,255,.96);z-index:9998;flex-direction:column;align-items:center;justify-content:center;gap:16px">
    <div style="width:52px;height:52px;border-radius:50%;background:var(--green-lt);display:flex;align-items:center;justify-content:center">
        <i class="ti ti-loader" style="font-size:26px;color:var(--green)"></i>
    </div>
    <div style="font-size:16px;font-weight:500;color:var(--green-deep)">正在送出維修單…</div>
    <div style="font-size:13px;color:var(--text-sec)">正在產生 PDF 並寄送 Email</div>
</div>

<!-- Email 確認 Modal -->
<div id="email-modal" style="display:none;position:fixed;inset:0;z-index:9999;background:rgba(0,0,0,.45);align-items:center;justify-content:center;padding:20px">
    <div style="background:#fff;border-radius:var(--radius-lg);width:100%;max-width:380px;overflow:hidden;box-shadow:0 8px 32px rgba(0,0,0,.18)">
        <div style="padding:14px 16px;border-bottom:0.5px solid var(--gray-bd);display:flex;align-items:center;gap:8px">
            <i class="ti ti-mail" style="font-size:18px;color:var(--green)"></i>
            <span style="font-size:15px;font-weight:500">確認寄送 Email</span>
        </div>
        <div style="padding:14px 16px">
            <div style="font-size:13px;color:var(--text-sec);margin-bottom:10px">PDF 維修單將寄送至以下 Email,如需更改請直接修改:</div>
            <input id="email-confirm-inp" type="email" inputmode="email"
                style="width:100%;font-size:14px;padding:10px 12px;border:1.5px solid var(--gray-bd2);border-radius:var(--radius-md);font-family:inherit;outline:none;color:var(--text)"
                placeholder="輸入 Email,留空則不寄送"
                onfocus="this.style.borderColor='var(--green)'"
                onblur="this.style.borderColor='var(--gray-bd2)'">
            <div id="email-hint-txt" style="font-size:11px;color:var(--text-tert);margin-top:5px">留空則不寄送 Email</div>
        </div>
        <div style="display:flex;gap:0;border-top:0.5px solid var(--gray-bd)">
            <button onclick="closeEmailModal()" style="flex:1;padding:13px;background:none;border:none;border-right:0.5px solid var(--gray-bd);font-size:14px;cursor:pointer;color:var(--text-sec);font-family:inherit">取消</button>
            <button onclick="confirmEmailAndSubmit()" style="flex:1;padding:13px;background:var(--green);border:none;font-size:14px;font-weight:500;cursor:pointer;color:#fff;font-family:inherit">確認送出</button>
        </div>
    </div>
</div>

<!-- 客戶簽名 Modal -->
<div id="csig-modal" class="sig-modal">
    <div class="sig-modal-bar">
        <span class="sig-modal-title">客戶簽名</span>
        <div style="display:flex;gap:10px">
            <button class="sig-clr-btn" onclick="clearSig('c')"><i class="ti ti-eraser" style="font-size:12px"></i> 清除</button>
            <button class="sig-ok-btn" onclick="confirmSig('c')"><i class="ti ti-check" style="font-size:12px"></i> 確認</button>
        </div>
    </div>
    <div class="sig-modal-area"><canvas id="csig-canvas" class="sig-canvas-el"></canvas><div id="csig-hint" class="sig-hint"><i class="ti ti-pencil" style="font-size:36px"></i><span>請在此區域簽名</span></div></div>
    <div class="sig-modal-tip">建議橫向握持手機簽名</div>
</div>

<!-- 底部 -->
<div class="bottom-bar">
    <button class="submit-btn" id="submit-btn" disabled onclick="doSubmit()">
        <i class="ti ti-send" style="font-size:16px"></i>
        <span id="submit-label">請完成所有必填項目</span>
    </button>
</div>

<script>
// ── PHP 資料注入 ──────────────────────
const G = <?= json_encode($G) ?>;
const TODAY = <?= json_encode($todayStr) ?>;

// ── 維修紀錄字數 ─────────────────────
function onRepairInput() {
    document.getElementById('char-num').textContent = document.getElementById('repair-content').value.length;
    const repairOk = document.getElementById('repair-content').value.trim().length > 0;
    if (!repairOk) {
        // 維修概況紀錄被清空時,連動取消已勾選的確認事項與工程師確認,避免邏輯矛盾
        document.querySelectorAll('.chk-item:not(#eng-chk-item) .chk-box').forEach(b=>b.classList.remove('on'));
        if (engConfirmed) {
            engConfirmed = false;
            document.getElementById('eng-chk-box').classList.remove('on');
        }
    }
    updateState();
}

// ── 確認勾選 ─────────────────────────
function toggleChk(row) {
    const repairOk = document.getElementById('repair-content').value.trim().length > 0;
    if (!repairOk) {
        alert('請先填寫維修概況紀錄');
        return;
    }
    row.querySelector('.chk-box').classList.toggle('on');
    updateState();
}

// ── 使用設備:多層選單 ────────────────
// 新增/回收 → 住戶端/據點 → 對應設備清單
const deviceUserOptions = ['光貓','光轉B','1000M無線分享器','100M無線分享器'];
const deviceBaseOptionsNew    = ['1000M集線器','光轉A','OLT','光分集器','主機'];
const deviceBaseOptionsRecycle = ['1000M集線器','100M集線器','光轉A','OLT','光分集器','主機'];
let devIdx = 0;

function addDeviceEntry() {
    const c = document.getElementById('device-container');
    const idx = devIdx++;
    const div = document.createElement('div');
    div.className = 'device-card'; div.dataset.idx = idx;
    div.innerHTML = `
        <div class="device-row">
            <select class="dev-sel dev-action" onchange="onDeviceActionChange(${idx})">
                <option value="">請選擇</option>
                <option value="新增">新增</option>
                <option value="回收">回收</option>
            </select>
            <select class="dev-sel dev-loc" style="display:none" onchange="onDeviceLocChange(${idx})">
                <option value="">請選擇地點</option>
                <option value="住戶端">住戶端</option>
                <option value="據點">據點</option>
            </select>
            <div class="sn-wrap dev-basenum-wrap" style="display:none">
                <input class="basenum-inp dev-basenum" type="text" placeholder="據點編號" maxlength="20"
                    inputmode="text" oninput="updateState()">
            </div>
            <button type="button" class="rm-btn" onclick="this.closest('.device-card').remove();updateState()">
                <i class="ti ti-x" style="font-size:13px"></i>
            </button>
        </div>
        <div id="dev-detail-${idx}"></div>
    `;
    c.appendChild(div);
}

function onDeviceActionChange(idx) {
    const card = document.querySelector(`.device-card[data-idx="${idx}"]`);
    const action = card.querySelector('.dev-action').value;
    const locSel = card.querySelector('.dev-loc');
    const basenumWrap = card.querySelector('.dev-basenum-wrap');
    const detail = document.getElementById(`dev-detail-${idx}`);
    if (!action) {
        locSel.style.display = 'none';
        locSel.value = '';
        basenumWrap.style.display = 'none';
        detail.innerHTML = '';
        updateState();
        return;
    }
    locSel.style.display = '';
    // 若已選過據點,依新的新增/回收狀態重新產生設備清單
    if (locSel.value === '據點') onDeviceLocChange(idx);
    updateState();
}

function onDeviceLocChange(idx) {
    const card = document.querySelector(`.device-card[data-idx="${idx}"]`);
    const action = card.querySelector('.dev-action').value;
    const loc = card.querySelector('.dev-loc').value;
    const detail = document.getElementById(`dev-detail-${idx}`);
    const basenumWrap = card.querySelector('.dev-basenum-wrap');
    detail.innerHTML = '';
    basenumWrap.style.display = (loc === '據點') ? 'flex' : 'none';
    if (!loc) { updateState(); return; }

    if (loc === '住戶端') {
        detail.innerHTML = `
            <div class="device-row sub">
                <select class="dev-sel dev-type">
                    ${deviceUserOptions.map(o=>`<option>${o}</option>`).join('')}
                </select>
                <div class="sn-wrap">
                    <input class="sn-inp dev-sn" type="text" placeholder="設備料號" maxlength="20"
                        inputmode="text" oninput="updateState()">
                </div>
            </div>`;
    } else if (loc === '據點') {
        const baseOptions = (action === '回收') ? deviceBaseOptionsRecycle : deviceBaseOptionsNew;
        detail.innerHTML = `
            <div class="device-row sub">
                <select class="dev-sel dev-type">
                    ${baseOptions.map(o=>`<option>${o}</option>`).join('')}
                </select>
                <div class="sn-wrap">
                    <input class="sn-inp dev-sn" type="text" placeholder="設備料號" maxlength="20"
                        inputmode="text" oninput="updateState()">
                </div>
            </div>`;
    }
    updateState();
}

// ── 收費 ─────────────────────────────
let feeMode = null; // 'free' | 'paid'
let feeIdx  = 0;
const feeTypes = ['維修費','周邊設備','裝機費','移機費','押金','傳輸費'];

function setFeeMode(mode) {
    feeMode = mode;
    document.getElementById('btn-free').className = 'fee-btn' + (mode==='free'?' sel-free':'');
    document.getElementById('btn-paid').className = 'fee-btn' + (mode==='paid'?' sel-paid':'');
    const sec = document.getElementById('fee-section');
    sec.style.display = mode==='paid' ? 'block' : 'none';
    if(mode==='paid' && document.querySelectorAll('.fee-item-row').length===0) addFeeRow();
    updateState();
}

function addFeeRow() {
    const c = document.getElementById('fee-container');
    const idx = feeIdx++;
    const div = document.createElement('div');
    div.className = 'fee-item-row'; div.dataset.idx = idx;
    div.innerHTML = `
        <select class="fee-type-sel" onchange="calcFees()">
            ${feeTypes.map(t=>`<option>${t}</option>`).join('')}
        </select>
        <input class="fee-amt-inp" type="text" placeholder="金額" inputmode="numeric" maxlength="6"
            oninput="this.value=this.value.replace(/\\D/g,'');calcFees()">
        <button type="button" class="rm-btn" onclick="this.closest('.fee-item-row').remove();calcFees()">
            <i class="ti ti-x" style="font-size:13px"></i>
        </button>`;
    c.appendChild(div);
}

function calcFees() {
    let total = 0, deposit = 0;
    document.querySelectorAll('.fee-item-row').forEach(row => {
        const type = row.querySelector('.fee-type-sel').value;
        const amt  = parseInt(row.querySelector('.fee-amt-inp').value) || 0;
        if(type === '押金') deposit += amt;
        else total += amt;
    });
    document.getElementById('fee-total').textContent = '$' + total.toLocaleString();
    const dr = document.getElementById('deposit-row');
    dr.style.display = deposit > 0 ? 'flex' : 'none';
    document.getElementById('deposit-total').textContent = '$' + deposit.toLocaleString();
    updateState();
}

// ── 簽名(僅客戶)───────────────────
let sigData = { c: null };
let sigStrokes = { c: false };
let drawing = false, currentSigKey = null;

function sigEl(k, sel) { return document.getElementById(k+'sig-'+sel); }

function initCanvas(k) {
    const modal = document.getElementById(k+'sig-modal');
    const area  = modal.querySelector('.sig-modal-area');
    const cv    = sigEl(k,'canvas');
    cv.width    = area.offsetWidth  || window.innerWidth;
    cv.height   = area.offsetHeight || (window.innerHeight - 110);
    const ctx   = cv.getContext('2d');
    ctx.strokeStyle='#111'; ctx.lineWidth=2.5; ctx.lineCap='round'; ctx.lineJoin='round';
}

function openSigModal(k) {
    const modal = document.getElementById(k+'sig-modal');
    modal.style.display='flex';
    document.body.style.overflow='hidden';
    currentSigKey = k;
    setTimeout(()=>{
        initCanvas(k);
        sigStrokes[k]=false;
        const cv = sigEl(k,'canvas');
        cv.getContext('2d').clearRect(0,0,cv.width,cv.height);
        sigEl(k,'hint').style.display='flex';
    },60);
}

function getPos(e, cv) {
    const r=cv.getBoundingClientRect(), s=e.touches?e.touches[0]:e;
    return { x:(s.clientX-r.left)*(cv.width/r.width), y:(s.clientY-r.top)*(cv.height/r.height) };
}

document.addEventListener('mousedown',  startDraw);
document.addEventListener('touchstart', startDraw, {passive:false});
function startDraw(e) {
    const k = currentSigKey; if(!k) return;
    const modal = document.getElementById(k+'sig-modal');
    if(modal.style.display!=='flex') return;
    const cv = sigEl(k,'canvas');
    if(!cv.contains(e.target) && e.target!==cv) return;
    e.preventDefault(); drawing=true;
    const p=getPos(e,cv), ctx=cv.getContext('2d');
    ctx.beginPath(); ctx.moveTo(p.x,p.y);
    sigEl(k,'hint').style.display='none';
    document.addEventListener('mousemove', onDraw);
    document.addEventListener('mouseup',   stopDraw);
    document.addEventListener('touchmove', onDraw, {passive:false});
    document.addEventListener('touchend',  stopDraw);
}
function onDraw(e) {
    if(!drawing||!currentSigKey) return; e.preventDefault();
    const cv=sigEl(currentSigKey,'canvas'), ctx=cv.getContext('2d');
    const p=getPos(e,cv); ctx.lineTo(p.x,p.y); ctx.stroke();
    sigStrokes[currentSigKey]=true;
}
function stopDraw() {
    drawing=false;
    document.removeEventListener('mousemove',onDraw); document.removeEventListener('mouseup',stopDraw);
    document.removeEventListener('touchmove',onDraw); document.removeEventListener('touchend',stopDraw);
}
function clearSig(k) {
    const cv=sigEl(k,'canvas'); cv.getContext('2d').clearRect(0,0,cv.width,cv.height);
    sigStrokes[k]=false; sigEl(k,'hint').style.display='flex';
}
function confirmSig(k) {
    if(!sigStrokes[k]){ alert('請先在方框內簽名'); return; }
    sigData[k] = sigEl(k,'canvas').toDataURL('image/png');
    document.getElementById(k+'sig-modal').style.display='none';
    document.body.style.overflow=''; currentSigKey=null;
    document.getElementById('sig-preview-img').src = sigData.c;
    document.getElementById('sig-preview-wrap').style.display='block';
    document.getElementById('sig-trigger').style.display='none';
    const b=document.getElementById('csig-badge');
    b.textContent='已簽名'; b.style.background='var(--green-lt)'; b.style.color='var(--green-deep)';
    updateState();
}
window.addEventListener('orientationchange',()=>{
    if(currentSigKey){ setTimeout(()=>initCanvas(currentSigKey),300); }
});

// ── 工程師確認勾選 ───────────────────
let engConfirmed = false;
function toggleEngChk() {
    // 取消勾選不受限制(讓工程師可以反悔重新確認)
    if (engConfirmed) {
        engConfirmed = false;
        document.getElementById('eng-chk-box').classList.toggle('on', engConfirmed);
        updateState();
        return;
    }
    const repairOk = document.getElementById('repair-content').value.trim().length > 0;
    const allChked = [...document.querySelectorAll('.chk-item:not(#eng-chk-item) .chk-box')].every(b=>b.classList.contains('on'));
    const feeOk    = feeMode !== null;
    const sigOk    = !!sigData.c;
    if (!repairOk)  { alert('請先填寫維修概況紀錄'); return; }
    if (!allChked)  { alert('請先完成上方確認事項勾選'); return; }
    if (!feeOk)     { alert('請先選擇收費狀態'); return; }
    if (!sigOk)     { alert('請先完成客戶簽名'); return; }
    engConfirmed = true;
    document.getElementById('eng-chk-box').classList.toggle('on', engConfirmed);
    updateState();
}

// ── 狀態更新 ─────────────────────────
function updateState() {
    const repairOk = document.getElementById('repair-content').value.trim().length > 0;
    const allChked = [...document.querySelectorAll('.chk-item:not(#eng-chk-item) .chk-box')].every(b=>b.classList.contains('on'));
    const feeOk    = feeMode !== null;
    const sigOk    = !!sigData.c;
    const allOk    = repairOk && allChked && feeOk && sigOk && engConfirmed;
    const btn      = document.getElementById('submit-btn');
    const lbl      = document.getElementById('submit-label');
    btn.disabled   = !allOk;
    if(allOk)              lbl.textContent = '送出維修單';
    else if(!repairOk)     lbl.textContent = '請填寫維修概況紀錄';
    else if(!allChked)     lbl.textContent = '請完成確認勾選';
    else if(!feeOk)        lbl.textContent = '請選擇收費狀態';
    else if(!sigOk)        lbl.textContent = '請完成客戶簽名';
    else                   lbl.textContent = '請工程師勾選確認表單';
}

// ── Email 確認 Modal ─────────────────
function doSubmit() {
    if(!sigData.c){ alert('請完成客戶簽名'); return; }
    if(!engConfirmed){ alert('請工程師勾選確認表單'); return; }
    const repairContent = document.getElementById('repair-content').value.trim();
    if(!repairContent){ alert('請填寫維修概況紀錄'); return; }

    // 開啟 Email 確認 Modal,預填原始 email
    const origEmail = G.email || '';
    const inp = document.getElementById('email-confirm-inp');
    inp.value = origEmail;
    const hint = document.getElementById('email-hint-txt');
    hint.textContent = origEmail ? '原始信箱:' + origEmail + '(可修改或清空)' : '未提供信箱,可手動填寫或留空不寄送';
    document.getElementById('email-modal').style.display = 'flex';
}

function closeEmailModal() {
    document.getElementById('email-modal').style.display = 'none';
}

function confirmEmailAndSubmit() {
    const finalEmail = document.getElementById('email-confirm-inp').value.trim();
    document.getElementById('email-modal').style.display = 'none';
    doActualSubmit(finalEmail);
}

function doActualSubmit(finalEmail) {
    document.getElementById('submitting-overlay').style.display='flex';
    document.querySelector('.bottom-bar').style.display='none';

    const repairContent = document.getElementById('repair-content').value.trim();

    const devices=[];
    document.querySelectorAll('.device-card').forEach(card=>{
        const action = card.querySelector('.dev-action')?.value || '';
        const loc    = card.querySelector('.dev-loc')?.value || '';
        const type   = card.querySelector('.dev-type')?.value || '';
        const sn     = card.querySelector('.dev-sn')?.value.trim() || '';
        const basenum= card.querySelector('.dev-basenum')?.value.trim() || '';
        if(action && loc && type){
            devices.push({action, loc, type, sn, basenum});
        }
    });
    const fees=[]; let feeTotal=0;
    if(feeMode==='paid'){
        document.querySelectorAll('.fee-item-row').forEach(row=>{
            const type=row.querySelector('.fee-type-sel').value;
            const amt=parseInt(row.querySelector('.fee-amt-inp').value)||0;
            fees.push({type,amt});
            if(type !== '押金') feeTotal+=amt;
        });
    }
    const fd = new FormData();
    fd.append('user_id',        G.user_id);
    fd.append('enname',         G.ENname);
    fd.append('apname',         G.APname);
    fd.append('repair_content', repairContent);
    fd.append('repair_date',    TODAY);
    fd.append('fee_mode',       feeMode);
    fd.append('fees_json',      JSON.stringify(fees));
    fd.append('fee_total',      feeTotal);
    fd.append('devices_json',   JSON.stringify(devices));
    fd.append('form_data',      JSON.stringify(G));
    fd.append('final_email',    finalEmail);  // 最終 email(可能是修改後的或空)
    const arr=sigData.c.split(','),mime=arr[0].match(/:(.*?);/)[1],bstr=atob(arr[1]);
    const ia=new Uint8Array(bstr.length); for(let i=0;i<bstr.length;i++) ia[i]=bstr.charCodeAt(i);
    fd.append('csig', new Blob([ia],{type:mime}), 'csig.png');

    fetch('repair_submit.php',{method:'POST',body:fd})
        .then(r=>{ if(!r.ok) throw new Error('伺服器錯誤 '+r.status); return r.json(); })
        .then(data=>{
            if(data.success){ window.location.replace('repair_done.php'); }
            else {
                document.getElementById('submitting-overlay').style.display='none';
                document.querySelector('.bottom-bar').style.display='';
                alert('送出失敗:'+(data.message||'請聯繫客服'));
            }
        })
        .catch(err=>{
            document.getElementById('submitting-overlay').style.display='none';
            document.querySelector('.bottom-bar').style.display='';
            alert('網路錯誤:'+err.message);
        });
}
</script>
</body>
</html>