後台調整
This commit is contained in:
parent
43a0a8ac93
commit
f027c4e523
74
Backstage/carousel_ads_add.html
Normal file
74
Backstage/carousel_ads_add.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>新增輪播廣告</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h2 class="mb-4">新增輪播廣告</h2>
|
||||
<form id="adForm">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">廣告標題</label>
|
||||
<input type="text" class="form-control" id="title" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">廣告內容</label>
|
||||
<textarea class="form-control" id="content" rows="3" required></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="image" class="form-label">圖片上傳</label>
|
||||
<input type="file" class="form-control" id="image" accept="image/*" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="startDate" class="form-label">開始日期</label>
|
||||
<input type="date" class="form-control" id="startDate" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="endDate" class="form-label">結束日期</label>
|
||||
<input type="date" class="form-control" id="endDate" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">狀態</label>
|
||||
<select class="form-select" id="status" required>
|
||||
<option value="">請選擇</option>
|
||||
<option value="啟用">啟用</option>
|
||||
<option value="停用">停用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sortOrder" class="form-label">排序</label>
|
||||
<input type="number" class="form-control" id="sortOrder" min="1" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">新增廣告</button>
|
||||
<a href="carousel_ads_list.html" class="btn btn-secondary ms-2">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("adForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("title", document.getElementById("title").value);
|
||||
formData.append("content", document.getElementById("content").value);
|
||||
formData.append("image", document.getElementById("image").files[0]);
|
||||
formData.append("startDate", document.getElementById("startDate").value);
|
||||
formData.append("endDate", document.getElementById("endDate").value);
|
||||
formData.append("status", document.getElementById("status").value);
|
||||
formData.append("sortOrder", document.getElementById("sortOrder").value);
|
||||
|
||||
console.log("新增的廣告資料:");
|
||||
for (let [key, value] of formData.entries()) {
|
||||
console.log(key + ": ", value);
|
||||
}
|
||||
|
||||
alert("廣告已新增(實際需串接 API 上傳圖檔與資料)");
|
||||
window.location.href = "ad_list.html";
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
94
Backstage/carousel_ads_edit.html
Normal file
94
Backstage/carousel_ads_edit.html
Normal file
@ -0,0 +1,94 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>編輯輪播廣告</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.preview-img {
|
||||
max-width: 200px;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h2 class="mb-4">編輯輪播廣告</h2>
|
||||
<form id="editForm">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">廣告標題</label>
|
||||
<input type="text" class="form-control" id="title" value="社區中秋禮盒廣告" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">廣告內容</label>
|
||||
<textarea class="form-control" id="content" rows="3" required>歡迎訂購社區專屬中秋禮盒!</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="image" class="form-label">更換圖片(選填)</label>
|
||||
<input type="file" class="form-control" id="image" accept="image/*">
|
||||
<img id="currentImage" src="https://via.placeholder.com/300x150?text=廣告2" alt="目前圖片" class="preview-img">
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="startDate" class="form-label">開始日期</label>
|
||||
<input type="date" class="form-control" id="startDate" value="2025-05-20" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="endDate" class="form-label">結束日期</label>
|
||||
<input type="date" class="form-control" id="endDate" value="2099-12-31" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">狀態</label>
|
||||
<select class="form-select" id="status" required>
|
||||
<option value="">請選擇</option>
|
||||
<option value="啟用">啟用</option>
|
||||
<option value="停用" selected>停用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sortOrder" class="form-label">排序</label>
|
||||
<input type="number" class="form-control" id="sortOrder" value="2" min="1" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">儲存修改</button>
|
||||
<a href="carousel_ads_list.html" class="btn btn-secondary ms-2">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("editForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("title", document.getElementById("title").value);
|
||||
formData.append("content", document.getElementById("content").value);
|
||||
if (document.getElementById("image").files[0]) {
|
||||
formData.append("image", document.getElementById("image").files[0]);
|
||||
}
|
||||
formData.append("startDate", document.getElementById("startDate").value);
|
||||
formData.append("endDate", document.getElementById("endDate").value);
|
||||
formData.append("status", document.getElementById("status").value);
|
||||
formData.append("sortOrder", document.getElementById("sortOrder").value);
|
||||
|
||||
console.log("修改後的廣告資料:");
|
||||
for (let [key, value] of formData.entries()) {
|
||||
console.log(key + ": ", value);
|
||||
}
|
||||
|
||||
alert("廣告資料已儲存(實際需串接 API)");
|
||||
window.location.href = "ad_list.html";
|
||||
});
|
||||
|
||||
// 圖片預覽功能
|
||||
document.getElementById("image").addEventListener("change", function () {
|
||||
const file = this.files[0];
|
||||
if (file) {
|
||||
const imgPreview = document.getElementById("currentImage");
|
||||
imgPreview.src = URL.createObjectURL(file);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
161
Backstage/carousel_ads_list.html
Normal file
161
Backstage/carousel_ads_list.html
Normal file
@ -0,0 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>輪播廣告列表</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.status-badge {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.4em 0.7em;
|
||||
}
|
||||
.ad-thumbnail {
|
||||
width: 120px;
|
||||
height: 60px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.sort-input {
|
||||
width: 70px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2>輪播廣告列表</h2>
|
||||
<a href="carousel_ads_add.html" class="btn btn-success">+ 新增廣告</a>
|
||||
</div>
|
||||
|
||||
<!-- 搜尋與控制列 -->
|
||||
<div class="row g-2 mb-3 align-items-end">
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<input type="text" id="searchName" class="form-control" placeholder="搜尋標題" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-2 col-lg-2">
|
||||
<button class="btn btn-outline-primary w-100 rounded-pill" onclick="window.alert('依據查詢關鍵字列出');">搜尋</button>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-2 d-flex justify-content-between align-items-center flex-wrap">
|
||||
<div class="d-flex gap-2 mb-2">
|
||||
<button class="btn btn-danger" onclick="deleteSelected()">刪除選取</button>
|
||||
<button class="btn btn-primary" onclick="saveSortChanges()">儲存排序變更</button>
|
||||
</div>
|
||||
|
||||
<div style="min-width: 200px;">
|
||||
<select id="sortOrder" class="form-select" onchange="sortTable()">
|
||||
<option value="asc">依排序:由小到大</option>
|
||||
<option value="desc">依排序:由大到小</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="adTable" class="table table-bordered table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th><input type="checkbox" onclick="toggleAll(this)" /></th>
|
||||
<th>縮圖</th>
|
||||
<th>標題</th>
|
||||
<th>期間</th>
|
||||
<th>狀態</th>
|
||||
<th>排序</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" /></td>
|
||||
<td><img src="https://via.placeholder.com/300x150?text=廣告1" class="ad-thumbnail" alt="廣告縮圖" /></td>
|
||||
<td>社區防火宣導</td>
|
||||
<td>2023/05/20 - 2024/12/20(過期)</td>
|
||||
<td><span class="badge bg-success status-badge">啟用</span></td>
|
||||
<td><input type="number" class="form-control form-control-sm sort-input" value="1" /></td>
|
||||
<td>
|
||||
<a href="carousel_ads_edit.html?id=1" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="if(confirm('確定要刪除這則廣告嗎?')) this.closest('tr').remove();">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" /></td>
|
||||
<td><img src="https://via.placeholder.com/300x150?text=廣告2" class="ad-thumbnail" alt="廣告縮圖" /></td>
|
||||
<td>社區中秋禮盒廣告</td>
|
||||
<td>2025/05/20 - 2099/12/31(顯示中)</td>
|
||||
<td><span class="badge bg-secondary status-badge">停用</span></td>
|
||||
<td><input type="number" class="form-control form-control-sm sort-input" value="2" /></td>
|
||||
<td>
|
||||
<a href="carousel_ads_edit.html?id=2" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="if(confirm('確定要刪除這則廣告嗎?')) this.closest('tr').remove();">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" /></td>
|
||||
<td><img src="https://via.placeholder.com/300x150?text=廣告2" class="ad-thumbnail" alt="廣告縮圖" /></td>
|
||||
<td>社區中秋禮盒廣告</td>
|
||||
<td>2025/05/20 - 2099/12/31(未到)</td>
|
||||
<td><span class="badge bg-secondary status-badge">停用</span></td>
|
||||
<td><input type="number" class="form-control form-control-sm sort-input" value="3" /></td>
|
||||
<td>
|
||||
<a href="carousel_ads_edit.html?id=2" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="if(confirm('確定要刪除這則廣告嗎?')) this.closest('tr').remove();">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- 更多資料列可加在這裡 -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>第 <span>1</span> 頁,共 <span>1</span> 頁</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary me-1">上一頁</button>
|
||||
<button class="btn btn-sm btn-outline-secondary">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function toggleAll(source) {
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
checkboxes.forEach(cb => (cb.checked = source.checked));
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('請先勾選要刪除的廣告');
|
||||
return;
|
||||
}
|
||||
if (!confirm(`確定要刪除 ${checkboxes.length} 則廣告嗎?`)) return;
|
||||
checkboxes.forEach(cb => cb.closest('tr').remove());
|
||||
}
|
||||
|
||||
function saveSortChanges() {
|
||||
const rows = document.querySelectorAll('#adTable tbody tr');
|
||||
rows.forEach(row => {
|
||||
const title = row.children[2].textContent.trim();
|
||||
const newSort = row.querySelector('.sort-input').value;
|
||||
console.log(`廣告:「${title}」新排序:${newSort}`);
|
||||
// 可在此加上 fetch/ajax 傳到後端
|
||||
});
|
||||
alert('排序變更已儲存(模擬)');
|
||||
}
|
||||
|
||||
function sortTable() {
|
||||
const order = document.getElementById('sortOrder').value;
|
||||
const table = document.getElementById('adTable');
|
||||
const tbody = table.querySelector('tbody');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const aValue = parseInt(a.querySelector('.sort-input').value);
|
||||
const bValue = parseInt(b.querySelector('.sort-input').value);
|
||||
return order === 'asc' ? aValue - bValue : bValue - aValue;
|
||||
});
|
||||
|
||||
tbody.innerHTML = '';
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
117
Backstage/feedback_edit.html
Normal file
117
Backstage/feedback_edit.html
Normal file
@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>意見回饋詳情</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.form-control-plaintext {
|
||||
border: 1px solid #ced4da; /* Optional: add a light border to plaintext for visual grouping */
|
||||
padding: .375rem .75rem;
|
||||
background-color: #e9ecef; /* Light gray to indicate non-editable */
|
||||
}
|
||||
textarea.form-control-plaintext {
|
||||
min-height: 100px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h2 class="mb-4">意見回饋詳情</h2>
|
||||
<form id="feedbackDetailForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>ID</strong></label>
|
||||
<input type="text" class="form-control-plaintext" id="feedbackIdDisplay" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>提交者</strong></label>
|
||||
<input type="text" class="form-control-plaintext" id="submitterDisplay" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>意見類型</strong></label>
|
||||
<input type="text" class="form-control-plaintext" id="typeDisplay" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>提交時間</strong></label>
|
||||
<input type="text" class="form-control-plaintext" id="timeDisplay" readonly>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><strong>內容</strong></label>
|
||||
<textarea class="form-control-plaintext" id="contentDisplay" rows="6" readonly></textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="statusSelect" class="form-label"><strong>處理狀態</strong></label>
|
||||
<select class="form-select" id="statusSelect" required>
|
||||
<option value="未讀">未讀</option>
|
||||
<option value="需處理">需處理</option>
|
||||
<option value="已處理">已處理</option>
|
||||
<option value="不需處理">不需處理</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">更新狀態</button>
|
||||
<a href="feedback_list.html" class="btn btn-secondary ms-2">返回列表</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Sample data - in a real application, this would come from a backend or be passed from the list page.
|
||||
// This structure should match what's used in feedback_admin_list.html
|
||||
const allFeedbackItems = [
|
||||
{ id: 1, submitter: "A101住戶", type: "建議", content: "社區中庭可以增加一些兒童遊樂設施,讓孩子們有更多玩樂的空間。", submissionTime: "2025/05/20 10:30", processingStatus: "未讀", anonymous: false },
|
||||
{ id: 2, submitter: "匿名", type: "問題", content: "地下室停車場B區靠近出口的燈光好像壞了,晚上有點暗,希望能盡快修復。", submissionTime: "2025/05/19 15:00", processingStatus: "已處理", anonymous: true },
|
||||
{ id: 3, submitter: "C305住戶 (林小姐)", type: "其他", content: "非常感謝上週管理員協助代收重要包裹,並且及時通知,服務很棒!", submissionTime: "2025/05/21 09:00", processingStatus: "不需處理", anonymous: false },
|
||||
{ id: 4, submitter: "B202住戶", type: "建議", content: "建議圖書室可以增加一些最新的雜誌和報紙。", submissionTime: "2025/05/21 11:15", processingStatus: "需處理", anonymous: false },
|
||||
{ id: 5, submitter: "匿名", type: "問題", content: "一樓大廳的飲水機好像出水有點小,麻煩檢查一下。", submissionTime: "2025/05/22 14:00", processingStatus: "未讀", anonymous: true },
|
||||
{ id: 6, submitter: "D110住戶 (王先生)", type: "建議", content: "垃圾集中處理區的分類標示可以再更清楚一點。", submissionTime: "2025/05/18 08:20", processingStatus: "已處理", anonymous: false },
|
||||
{ id: 7, submitter: "匿名", type: "其他", content: "週末的社區電影院活動很棒,希望未來能多舉辦。", submissionTime: "2025/05/17 19:30", processingStatus: "不需處理", anonymous: true },
|
||||
{ id: 8, submitter: "E201住戶", type: "問題", content: "健身房的跑步機有異常聲音,請派人檢修。", submissionTime: "2025/05/23 09:00", processingStatus: "需處理", anonymous: false },
|
||||
];
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const feedbackId = parseInt(urlParams.get('id'));
|
||||
|
||||
if (!feedbackId) {
|
||||
alert('未指定意見回饋 ID!');
|
||||
window.location.href = 'feedback_list.html';
|
||||
return;
|
||||
}
|
||||
|
||||
const feedbackItem = allFeedbackItems.find(item => item.id === feedbackId);
|
||||
|
||||
if (feedbackItem) {
|
||||
document.getElementById('feedbackIdDisplay').value = feedbackItem.id;
|
||||
document.getElementById('submitterDisplay').value = feedbackItem.anonymous ? '匿名' : feedbackItem.submitter;
|
||||
document.getElementById('typeDisplay').value = feedbackItem.type;
|
||||
document.getElementById('timeDisplay').value = feedbackItem.submissionTime;
|
||||
document.getElementById('contentDisplay').value = feedbackItem.content;
|
||||
document.getElementById('statusSelect').value = feedbackItem.processingStatus;
|
||||
} else {
|
||||
alert('找不到該意見回饋!');
|
||||
window.location.href = 'feedback_admin_list.html';
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("feedbackDetailForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const feedbackId = document.getElementById('feedbackIdDisplay').value;
|
||||
const newStatus = document.getElementById("statusSelect").value;
|
||||
|
||||
console.log("更新意見回饋狀態:");
|
||||
console.log("ID:", feedbackId);
|
||||
console.log("新狀態:", newStatus);
|
||||
|
||||
// Here you would typically send this data to a backend API
|
||||
// For demonstration, we'll just show an alert and redirect.
|
||||
|
||||
alert(`意見 ID ${feedbackId} 的狀態已更新為 "${newStatus}" (此為模擬操作)`);
|
||||
window.location.href = "feedback_admin_list.html";
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
171
Backstage/feedback_list.html
Normal file
171
Backstage/feedback_list.html
Normal file
@ -0,0 +1,171 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>意見回饋管理</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.content-summary {
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h2>意見回饋列表</h2>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3 align-items-center">
|
||||
<div class="col-sm-6 col-md-3 col-lg-3">
|
||||
<input type="text" id="searchContent" class="form-control" placeholder="搜尋內容摘要">
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3 col-lg-2">
|
||||
<select id="filterType" class="form-select">
|
||||
<option value="">所有意見類型</option>
|
||||
<option value="建議">建議</option>
|
||||
<option value="問題">問題</option>
|
||||
<option value="其他">其他</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3 col-lg-2">
|
||||
<select id="filterStatus" class="form-select">
|
||||
<option value="">所有處理狀態</option>
|
||||
<option value="未讀">未讀</option>
|
||||
<option value="需處理">需處理</option>
|
||||
<option value="已處理">已處理</option>
|
||||
<option value="不需處理">不需處理</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-3 col-lg-2">
|
||||
<button class="btn btn-outline-primary w-100 rounded-pill" onclick="applyFilters()">搜尋/篩選</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-bordered table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>提交者</th>
|
||||
<th>意見類型</th>
|
||||
<th>內容摘要</th>
|
||||
<th>提交時間</th>
|
||||
<th>處理狀態</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="feedbackTableBody"></tbody>
|
||||
</table>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>第 <span id="currentPage">1</span> 頁,共 <span id="totalPages">1</span> 頁</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary me-1" id="prevBtn">上一頁</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="nextBtn">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const allFeedbackItems = [
|
||||
{ id: 1, submitter: "A101住戶", type: "建議", content: "社區中庭可以增加一些兒童遊樂設施,讓孩子們有更多玩樂的空間。", submissionTime: "2025/05/20 10:30", processingStatus: "未讀", anonymous: false },
|
||||
{ id: 2, submitter: "匿名", type: "問題", content: "地下室停車場B區靠近出口的燈光好像壞了,晚上有點暗,希望能盡快修復。", submissionTime: "2025/05/19 15:00", processingStatus: "已處理", anonymous: true },
|
||||
{ id: 3, submitter: "C305住戶 (林小姐)", type: "其他", content: "非常感謝上週管理員協助代收重要包裹,並且及時通知,服務很棒!", submissionTime: "2025/05/21 09:00", processingStatus: "不需處理", anonymous: false },
|
||||
{ id: 4, submitter: "B202住戶", type: "建議", content: "建議圖書室可以增加一些最新的雜誌和報紙。", submissionTime: "2025/05/21 11:15", processingStatus: "需處理", anonymous: false },
|
||||
{ id: 5, submitter: "匿名", type: "問題", content: "一樓大廳的飲水機好像出水有點小,麻煩檢查一下。", submissionTime: "2025/05/22 14:00", processingStatus: "未讀", anonymous: true },
|
||||
{ id: 6, submitter: "D110住戶 (王先生)", type: "建議", content: "垃圾集中處理區的分類標示可以再更清楚一點。", submissionTime: "2025/05/18 08:20", processingStatus: "已處理", anonymous: false },
|
||||
{ id: 7, submitter: "匿名", type: "其他", content: "週末的社區電影院活動很棒,希望未來能多舉辦。", submissionTime: "2025/05/17 19:30", processingStatus: "不需處理", anonymous: true },
|
||||
{ id: 8, submitter: "E201住戶", type: "問題", content: "健身房的跑步機有異常聲音,請派人檢修。", submissionTime: "2025/05/23 09:00", processingStatus: "需處理", anonymous: false },
|
||||
];
|
||||
|
||||
let filteredFeedbackItems = [...allFeedbackItems];
|
||||
const pageSize = 5;
|
||||
let currentPage = 1;
|
||||
|
||||
function getStatusBadge(status) {
|
||||
switch (status) {
|
||||
case "未讀":
|
||||
return `<span class="badge bg-warning text-dark">${status}</span>`; // Yellow
|
||||
case "需處理":
|
||||
return `<span class="badge bg-danger">${status}</span>`; // Red
|
||||
case "已處理":
|
||||
return `<span class="badge bg-success">${status}</span>`; // Green
|
||||
case "不需處理":
|
||||
return `<span class="badge bg-secondary">${status}</span>`; // Gray
|
||||
default:
|
||||
return `<span class="badge bg-light text-dark">${status}</span>`; // Default/fallback
|
||||
}
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
const tbody = document.getElementById("feedbackTableBody");
|
||||
tbody.innerHTML = "";
|
||||
|
||||
const startIndex = (currentPage - 1) * pageSize;
|
||||
const pageItems = filteredFeedbackItems.slice(startIndex, startIndex + pageSize);
|
||||
|
||||
pageItems.forEach(item => {
|
||||
const tr = document.createElement("tr");
|
||||
const contentSummary = item.content.length > 50 ? item.content.substring(0, 50) + "..." : item.content;
|
||||
const submitterDisplay = item.anonymous ? "匿名" : item.submitter;
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${submitterDisplay}</td>
|
||||
<td>${item.type}</td>
|
||||
<td class="content-summary" title="${item.content}">${contentSummary}</td>
|
||||
<td>${item.submissionTime}</td>
|
||||
<td>${getStatusBadge(item.processingStatus)}</td>
|
||||
<td><a href="feedback_edit.html?id=${item.id}" class="btn btn-outline-secondary btn-sm">查看詳情</a></td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
const totalPages = Math.ceil(filteredFeedbackItems.length / pageSize);
|
||||
document.getElementById("currentPage").textContent = currentPage;
|
||||
document.getElementById("totalPages").textContent = totalPages || 1;
|
||||
|
||||
document.getElementById("prevBtn").disabled = currentPage === 1;
|
||||
document.getElementById("nextBtn").disabled = currentPage === totalPages || totalPages === 0;
|
||||
}
|
||||
|
||||
function applyFilters() {
|
||||
const searchTerm = document.getElementById("searchContent").value.toLowerCase();
|
||||
const selectedType = document.getElementById("filterType").value;
|
||||
const selectedStatus = document.getElementById("filterStatus").value;
|
||||
|
||||
filteredFeedbackItems = allFeedbackItems.filter(item => {
|
||||
const contentMatch = item.content.toLowerCase().includes(searchTerm);
|
||||
const typeMatch = selectedType ? item.type === selectedType : true;
|
||||
const statusMatch = selectedStatus ? item.processingStatus === selectedStatus : true;
|
||||
return contentMatch && typeMatch && statusMatch;
|
||||
});
|
||||
currentPage = 1; // Reset to first page after filtering
|
||||
renderTable();
|
||||
if (filteredFeedbackItems.length === 0) {
|
||||
window.alert('沒有找到符合條件的意見回饋。');
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("prevBtn").addEventListener("click", () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
renderTable();
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("nextBtn").addEventListener("click", () => {
|
||||
const totalPages = Math.ceil(filteredFeedbackItems.length / pageSize);
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
renderTable();
|
||||
}
|
||||
});
|
||||
|
||||
renderTable(); // Initial render
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
69
Backstage/marquee_ads_add.html
Normal file
69
Backstage/marquee_ads_add.html
Normal file
@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>新增跑馬燈廣告</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h2 class="mb-4">新增跑馬燈廣告</h2>
|
||||
<form id="adForm">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">識別名稱 (方便管理)</label>
|
||||
<input type="text" class="form-control" id="name" placeholder="例如:夏季促銷活動">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="marqueeContent" class="form-label">跑馬燈內容</label>
|
||||
<textarea class="form-control" id="marqueeContent" rows="3" placeholder="請輸入要滾動顯示的文字訊息" required></textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="startDate" class="form-label">開始日期</label>
|
||||
<input type="date" class="form-control" id="startDate" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="endDate" class="form-label">結束日期</label>
|
||||
<input type="date" class="form-control" id="endDate" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">狀態</label>
|
||||
<select class="form-select" id="status" required>
|
||||
<option value="">請選擇</option>
|
||||
<option value="啟用">啟用</option>
|
||||
<option value="停用">停用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sortOrder" class="form-label">排序</label>
|
||||
<input type="number" class="form-control" id="sortOrder" min="1" value="10" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success">新增跑馬燈</button>
|
||||
<a href="marquee_ads_list.html" class="btn btn-secondary ms-2">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("adForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("name", document.getElementById("name").value);
|
||||
formData.append("marqueeContent", document.getElementById("marqueeContent").value);
|
||||
formData.append("startDate", document.getElementById("startDate").value);
|
||||
formData.append("endDate", document.getElementById("endDate").value);
|
||||
formData.append("status", document.getElementById("status").value);
|
||||
formData.append("sortOrder", document.getElementById("sortOrder").value);
|
||||
|
||||
console.log("新增的跑馬燈廣告資料:");
|
||||
for (let [key, value] of formData.entries()) {
|
||||
console.log(key + ": ", value);
|
||||
}
|
||||
|
||||
alert("跑馬燈廣告已新增(實際需串接 API 儲存資料)");
|
||||
window.location.href = "marquee_ads_list.html";
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
69
Backstage/marquee_ads_edit.html
Normal file
69
Backstage/marquee_ads_edit.html
Normal file
@ -0,0 +1,69 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>編輯跑馬燈廣告</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h2 class="mb-4">編輯跑馬燈廣告</h2>
|
||||
<form id="editForm">
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">識別名稱 (方便管理)</label>
|
||||
<input type="text" class="form-control" id="name" value="系統維護公告" placeholder="例如:夏季促銷活動">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="marqueeContent" class="form-label">跑馬燈內容</label>
|
||||
<textarea class="form-control" id="marqueeContent" rows="3" required>本網站將於下週三凌晨 02:00 至 04:00 進行系統維護。</textarea>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="startDate" class="form-label">開始日期</label>
|
||||
<input type="date" class="form-control" id="startDate" value="2025-05-25" required>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="endDate" class="form-label">結束日期</label>
|
||||
<input type="date" class="form-control" id="endDate" value="2025-05-28" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="status" class="form-label">狀態</label>
|
||||
<select class="form-select" id="status" required>
|
||||
<option value="">請選擇</option>
|
||||
<option value="啟用">啟用</option>
|
||||
<option value="停用" selected>停用</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="sortOrder" class="form-label">排序</label>
|
||||
<input type="number" class="form-control" id="sortOrder" value="2" min="1" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">儲存修改</button>
|
||||
<a href="marquee_ads_list.html" class="btn btn-secondary ms-2">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.getElementById("editForm").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("name", document.getElementById("name").value);
|
||||
formData.append("marqueeContent", document.getElementById("marqueeContent").value);
|
||||
formData.append("startDate", document.getElementById("startDate").value);
|
||||
formData.append("endDate", document.getElementById("endDate").value);
|
||||
formData.append("status", document.getElementById("status").value);
|
||||
formData.append("sortOrder", document.getElementById("sortOrder").value);
|
||||
|
||||
console.log("修改後的跑馬燈廣告資料:");
|
||||
for (let [key, value] of formData.entries()) {
|
||||
console.log(key + ": ", value);
|
||||
}
|
||||
|
||||
alert("跑馬燈廣告資料已儲存(實際需串接 API)");
|
||||
window.location.href = "marquee_ads_list.html";
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
161
Backstage/marquee_ads_list.html
Normal file
161
Backstage/marquee_ads_list.html
Normal file
@ -0,0 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>跑馬燈廣告列表</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.status-badge {
|
||||
font-size: 0.9rem;
|
||||
padding: 0.4em 0.7em;
|
||||
}
|
||||
.content-snippet {
|
||||
max-width: 300px; /* Adjusted width slightly */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.sort-input {
|
||||
width: 70px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h2>跑馬燈廣告列表</h2>
|
||||
<a href="marquee_ads_add.html" class="btn btn-success">+ 新增跑馬燈</a>
|
||||
</div>
|
||||
|
||||
<div class="row g-2 mb-3 align-items-end">
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<input type="text" id="searchName" class="form-control" placeholder="搜尋識別名稱或內容" />
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-2 col-lg-2">
|
||||
<button class="btn btn-outline-primary w-100 rounded-pill" onclick="window.alert('依據查詢關鍵字列出跑馬燈廣告');">搜尋</button>
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-2 d-flex justify-content-between align-items-center flex-wrap">
|
||||
<div class="d-flex gap-2 mb-2">
|
||||
<button class="btn btn-danger" onclick="deleteSelected()">刪除選取</button>
|
||||
<button class="btn btn-primary" onclick="saveSortChanges()">儲存排序變更</button>
|
||||
</div>
|
||||
|
||||
<div style="min-width: 200px;">
|
||||
<select id="sortOrder" class="form-select" onchange="sortTable()">
|
||||
<option value="asc">依排序:由小到大</option>
|
||||
<option value="desc">依排序:由大到小</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="adTable" class="table table-bordered table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th><input type="checkbox" onclick="toggleAll(this)" /></th>
|
||||
<th>識別名稱</th>
|
||||
<th>跑馬燈內容</th>
|
||||
<th>期間</th>
|
||||
<th>狀態</th>
|
||||
<th>排序</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" /></td>
|
||||
<td>春季特賣會</td>
|
||||
<td class="content-snippet">全店商品下殺五折起,滿千再送百元折價券!</td>
|
||||
<td>2025/06/01 - 2025/06/30(過期)</td>
|
||||
<td><span class="badge bg-success status-badge">啟用</span></td>
|
||||
<td><input type="number" class="form-control form-control-sm sort-input" value="1" /></td>
|
||||
<td>
|
||||
<a href="marquee_ads_edit.html?id=1" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="if(confirm('確定要刪除這則跑馬燈廣告嗎?')) this.closest('tr').remove();">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" /></td>
|
||||
<td>系統維護公告</td>
|
||||
<td class="content-snippet">本網站將於下週三凌晨 02:00 至 04:00 進行系統維護。</td>
|
||||
<td>2023/05/25 - 2025/05/28(未到)</td>
|
||||
<td><span class="badge bg-secondary status-badge">停用</span></td>
|
||||
<td><input type="number" class="form-control form-control-sm sort-input" value="2" /></td>
|
||||
<td>
|
||||
<a href="marquee_ads_edit.html?id=2" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="if(confirm('確定要刪除這則跑馬燈廣告嗎?')) this.closest('tr').remove();">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" /></td>
|
||||
<td>系統維護公告</td>
|
||||
<td class="content-snippet">本網站將於下週三凌晨 02:00 至 04:00 進行系統維護。</td>
|
||||
<td>2023/05/25 - 2025/05/28(顯示中)</td>
|
||||
<td><span class="badge bg-secondary status-badge">停用</span></td>
|
||||
<td><input type="number" class="form-control form-control-sm sort-input" value="3" /></td>
|
||||
<td>
|
||||
<a href="marquee_ads_edit.html?id=2" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="if(confirm('確定要刪除這則跑馬燈廣告嗎?')) this.closest('tr').remove();">刪除</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>第 <span>1</span> 頁,共 <span>1</span> 頁</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary me-1">上一頁</button>
|
||||
<button class="btn btn-sm btn-outline-secondary">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
function toggleAll(source) {
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
checkboxes.forEach(cb => (cb.checked = source.checked));
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
|
||||
if (checkboxes.length === 0) {
|
||||
alert('請先勾選要刪除的跑馬燈廣告');
|
||||
return;
|
||||
}
|
||||
if (!confirm(`確定要刪除 ${checkboxes.length} 則跑馬燈廣告嗎?`)) return;
|
||||
checkboxes.forEach(cb => cb.closest('tr').remove());
|
||||
}
|
||||
|
||||
function saveSortChanges() {
|
||||
const rows = document.querySelectorAll('#adTable tbody tr');
|
||||
rows.forEach(row => {
|
||||
// '識別名稱' is the second child (index 1)
|
||||
const adName = row.children[1].textContent.trim();
|
||||
const newSort = row.querySelector('.sort-input').value;
|
||||
console.log(`跑馬燈廣告:「${adName}」新排序:${newSort}`);
|
||||
// 可在此加上 fetch/ajax 傳到後端
|
||||
});
|
||||
alert('排序變更已儲存(模擬)');
|
||||
}
|
||||
|
||||
function sortTable() {
|
||||
const order = document.getElementById('sortOrder').value;
|
||||
const table = document.getElementById('adTable');
|
||||
const tbody = table.querySelector('tbody');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const aValue = parseInt(a.querySelector('.sort-input').value);
|
||||
const bValue = parseInt(b.querySelector('.sort-input').value);
|
||||
return order === 'asc' ? aValue - bValue : bValue - aValue;
|
||||
});
|
||||
|
||||
tbody.innerHTML = '';
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -4,10 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<title>居民對話訊息</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.chat-box {
|
||||
height: 70vh;
|
||||
@ -29,7 +26,7 @@
|
||||
margin: 0.25rem 0;
|
||||
border-radius: 1rem;
|
||||
max-width: 75%;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
.resident-msg {
|
||||
background-color: #ffffff;
|
||||
@ -41,19 +38,14 @@
|
||||
}
|
||||
.canned-replies button {
|
||||
margin: 0.25rem 0.25rem 0 0;
|
||||
|
||||
}
|
||||
#messageInput {
|
||||
|
||||
height: calc(2.25rem + 0.5rem);
|
||||
}
|
||||
#sendBtn {
|
||||
|
||||
padding: 0.35rem 1rem;
|
||||
}
|
||||
/* 上傳按鈕樣式 */
|
||||
.btn-upload {
|
||||
|
||||
padding: 0 0.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -68,102 +60,109 @@
|
||||
.btn-upload:hover {
|
||||
background-color: #e7f1ff;
|
||||
}
|
||||
/* 檔名顯示 */
|
||||
.file-name {
|
||||
margin-top: 0.3rem;
|
||||
font-style: italic;
|
||||
color: #555;
|
||||
|
||||
min-height: 1.2rem;
|
||||
}
|
||||
/* 調整輸入組合整體圓角 */
|
||||
.input-group>.btn-upload {
|
||||
.input-group > .btn-upload {
|
||||
border-right: none;
|
||||
}
|
||||
.input-group>.form-control {
|
||||
.input-group > .form-control {
|
||||
border-radius: 0 0.375rem 0.375rem 0;
|
||||
}
|
||||
.msg-time {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
color: #888;
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
.date-divider {
|
||||
text-align: center;
|
||||
font-size: 0.85rem;
|
||||
color: #555;
|
||||
margin: 1rem 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<div
|
||||
class="d-flex justify-content-between align-items-center mb-3"
|
||||
>
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h4 id="residentName">王小明</h4>
|
||||
<a href="message_list.html" class="btn btn-outline-secondary"
|
||||
>← 返回列表</a
|
||||
>
|
||||
<a href="message_list.html" class="btn btn-outline-secondary">← 返回列表</a>
|
||||
</div>
|
||||
|
||||
<div class="chat-box mb-3" id="chatBox">
|
||||
<div class="date-divider" id="todayDateDivider"></div>
|
||||
|
||||
<div class="message-left">
|
||||
<div class="message resident-msg">你好,今天有包裹嗎?</div>
|
||||
<div class="message resident-msg">
|
||||
你好,今天有包裹嗎?
|
||||
<span class="msg-time">09:00</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-right">
|
||||
<div class="message admin-msg">您好,有一個郵件已放置櫃台。</div>
|
||||
<div class="message admin-msg">
|
||||
您好,有一個郵件已放置櫃台。
|
||||
<span class="msg-time">09:02</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-left">
|
||||
<div class="message resident-msg">好的,謝謝您。</div>
|
||||
<div class="message resident-msg">
|
||||
好的,謝謝您。
|
||||
<span class="msg-time">09:03</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="message-right">
|
||||
<div class="message admin-msg">不客氣,祝您愉快!</div>
|
||||
<div class="message admin-msg">
|
||||
不客氣,祝您愉快!
|
||||
<span class="msg-time">09:04</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 罐頭訊息選單 -->
|
||||
<!-- 快速罐頭訊息 -->
|
||||
<div class="canned-replies mb-2">
|
||||
<small class="text-muted">快速訊息:</small><br />
|
||||
<button class="btn btn-sm btn-outline-primary">
|
||||
您好,請問有什麼需要協助的?
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary">
|
||||
已為您處理,請稍候查看。
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary">
|
||||
明天早上會有維修人員前來。
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary">
|
||||
若有其他問題,歡迎再聯繫我們。
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary">您好,請問有什麼需要協助的?</button>
|
||||
<button class="btn btn-sm btn-outline-primary">已為您處理,請稍候查看。</button>
|
||||
<button class="btn btn-sm btn-outline-primary">明天早上會有維修人員前來。</button>
|
||||
<button class="btn btn-sm btn-outline-primary">若有其他問題,歡迎再聯繫我們。</button>
|
||||
</div>
|
||||
|
||||
<!-- 輸入區:上傳按鈕 + 輸入框 + 送出按鈕 -->
|
||||
<!-- 訊息輸入區 -->
|
||||
<div class="input-group mb-1">
|
||||
<label for="fileUpload" class="btn-upload" title="上傳照片附件">📷</label>
|
||||
<input
|
||||
type="file"
|
||||
id="fileUpload"
|
||||
accept="image/*"
|
||||
style="display: none"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
placeholder="輸入訊息..."
|
||||
id="messageInput"
|
||||
/>
|
||||
<button class="btn btn-primary" type="button" id="sendBtn">
|
||||
送出
|
||||
</button>
|
||||
<input type="file" id="fileUpload" accept="image/*" style="display: none" />
|
||||
<input type="text" class="form-control" placeholder="輸入訊息..." id="messageInput" />
|
||||
<button class="btn btn-primary" type="button" id="sendBtn">送出</button>
|
||||
</div>
|
||||
<!-- 顯示已選檔案名稱 -->
|
||||
<div id="fileName" class="file-name"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 點擊罐頭訊息時自動填入輸入框
|
||||
// 設定今天日期標籤
|
||||
const today = new Date();
|
||||
const todayStr = today.toLocaleDateString("zh-Hant", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
weekday: "long"
|
||||
});
|
||||
document.getElementById("todayDateDivider").textContent = "今天 - " + todayStr;
|
||||
|
||||
// 點選快速訊息填入輸入框
|
||||
document.querySelectorAll(".canned-replies button").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
document.getElementById("messageInput").value = btn.textContent;
|
||||
});
|
||||
});
|
||||
|
||||
// 顯示已選檔案名稱
|
||||
// 顯示上傳檔案名稱
|
||||
const fileInput = document.getElementById("fileUpload");
|
||||
const fileNameDisplay = document.getElementById("fileName");
|
||||
|
||||
fileInput.addEventListener("change", () => {
|
||||
if (fileInput.files.length > 0) {
|
||||
fileNameDisplay.textContent = "已選擇檔案: " + fileInput.files[0].name;
|
||||
@ -172,17 +171,43 @@
|
||||
}
|
||||
});
|
||||
|
||||
// 模擬送出訊息功能
|
||||
// 模擬送出訊息
|
||||
document.getElementById("sendBtn").addEventListener("click", () => {
|
||||
const msg = document.getElementById("messageInput").value.trim();
|
||||
if (msg) {
|
||||
alert("(模擬送出)訊息內容: " + msg);
|
||||
document.getElementById("messageInput").value = "";
|
||||
fileInput.value = "";
|
||||
fileNameDisplay.textContent = "";
|
||||
} else {
|
||||
const fileName = fileInput.files.length > 0 ? fileInput.files[0].name : "";
|
||||
if (!msg && !fileName) {
|
||||
alert("請輸入訊息或上傳圖片!");
|
||||
return;
|
||||
}
|
||||
|
||||
const chatBox = document.getElementById("chatBox");
|
||||
|
||||
// 建立訊息元素
|
||||
const msgContainer = document.createElement("div");
|
||||
msgContainer.className = "message-right";
|
||||
|
||||
const msgBubble = document.createElement("div");
|
||||
msgBubble.className = "message admin-msg";
|
||||
msgBubble.innerHTML = `${msg ? msg : ""}${fileName ? "<br><em>📎 " + fileName + "</em>" : ""}`;
|
||||
|
||||
const timeSpan = document.createElement("span");
|
||||
timeSpan.className = "msg-time";
|
||||
const now = new Date();
|
||||
const hour = now.getHours().toString().padStart(2, "0");
|
||||
const minute = now.getMinutes().toString().padStart(2, "0");
|
||||
timeSpan.textContent = `${hour}:${minute}`;
|
||||
|
||||
msgBubble.appendChild(timeSpan);
|
||||
msgContainer.appendChild(msgBubble);
|
||||
chatBox.appendChild(msgContainer);
|
||||
|
||||
// 清空輸入
|
||||
document.getElementById("messageInput").value = "";
|
||||
fileInput.value = "";
|
||||
fileNameDisplay.textContent = "";
|
||||
|
||||
// 自動捲到最底
|
||||
chatBox.scrollTop = chatBox.scrollHeight;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<style>
|
||||
/* 只針對狀態欄位中的 badge 進行字體放大 */
|
||||
.status-badge {
|
||||
font-size: 1rem;
|
||||
font-size: 0.9rem;
|
||||
padding: 0.6em 0.9em;
|
||||
}
|
||||
</style>
|
||||
|
@ -6,166 +6,188 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { display: flex; flex-direction: column; min-height: 100vh; margin: 0; }
|
||||
.container.mt-4 { flex-grow: 1; padding-bottom: 70px; }
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
.container.mt-4 {
|
||||
flex-grow: 1;
|
||||
padding-bottom: 70px;
|
||||
}
|
||||
nav.pagination-fixed {
|
||||
position: fixed; left: 0; bottom: 0; width: 100%;
|
||||
background-color: #f8f9fa; padding: 10px 0; border-top: 1px solid #dee2e6; z-index: 1000;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid #dee2e6;
|
||||
z-index: 1000;
|
||||
}
|
||||
nav.pagination-fixed .pagination { margin-bottom: 0; }
|
||||
.btn {
|
||||
font-weight: 500; font-size: 1rem;
|
||||
padding: 0.5rem 0.75rem; transition: all 0.2s ease-in-out;
|
||||
nav.pagination-fixed .pagination {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.btn:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0,0,0,0.1); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<h2 class="mb-4">報修廠商</h2>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-4 col-md-2 col-lg-1">
|
||||
<a href="repair_firm_add.html" class="btn btn-outline-success w-100 rounded-pill">新增</a>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 col-lg-1">
|
||||
<a href="repair_firm_import.html" class="btn btn-outline-info w-100 rounded-pill">匯入</a>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 col-lg-1">
|
||||
<button class="btn btn-outline-secondary w-100 rounded-pill" onclick="exportVendors()">匯出</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mt-4">
|
||||
<h2 class="mb-4">報修廠商</h2>
|
||||
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-sm-6 col-md-3 col-lg-2">
|
||||
<input type="text" id="searchName" class="form-control" placeholder="搜尋廠商名稱">
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<input type="text" id="searchAddress" class="form-control" placeholder="搜尋地址">
|
||||
</div>
|
||||
<div class="col-6 col-md-2 col-lg-2">
|
||||
<button class="btn btn-outline-primary w-100 rounded-pill" onclick="search()">搜尋</button>
|
||||
</div>
|
||||
<div class="col-6 col-md-2 col-lg-1">
|
||||
<button class="btn btn-outline-danger w-100 rounded-pill" onclick="deleteSelected()">刪除勾選</button>
|
||||
</div>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-4 col-md-2 col-lg-1">
|
||||
<a href="repair_firm_add.html" class="btn btn-outline-success w-100 rounded-pill btn-sm">新增</a>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 col-lg-1">
|
||||
<a href="repair_firm_import.html" class="btn btn-outline-info w-100 rounded-pill btn-sm">匯入</a>
|
||||
</div>
|
||||
<div class="col-4 col-md-2 col-lg-1">
|
||||
<button class="btn btn-outline-secondary w-100 rounded-pill btn-sm" onclick="exportVendors()">匯出</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll" onclick="toggleAll(this)"></th>
|
||||
<th>編號</th>
|
||||
<th>廠商名稱</th>
|
||||
<th>聯絡人</th>
|
||||
<th>聯絡電話</th>
|
||||
<th>地址</th>
|
||||
<th>服務項目</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="vendorTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav class="pagination-fixed">
|
||||
<ul class="pagination justify-content-center" id="pagination"></ul>
|
||||
</nav>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-sm-6 col-md-3 col-lg-2">
|
||||
<input type="text" id="searchName" class="form-control" placeholder="搜尋廠商名稱">
|
||||
</div>
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<input type="text" id="searchAddress" class="form-control" placeholder="搜尋地址">
|
||||
</div>
|
||||
<div class="col-6 col-md-2 col-lg-2">
|
||||
<button class="btn btn-outline-primary w-100 rounded-pill btn-sm" onclick="search()">搜尋</button>
|
||||
</div>
|
||||
<div class="col-6 col-md-2 col-lg-1">
|
||||
<button class="btn btn-outline-danger w-100 rounded-pill btn-sm" onclick="deleteSelected()">刪除勾選</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const pageSize = 10;
|
||||
let currentPage = 1;
|
||||
<table class="table table-bordered table-hover align-middle">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll" onclick="toggleAll(this)"></th>
|
||||
<th>編號</th>
|
||||
<th>廠商名稱</th>
|
||||
<th>聯絡人</th>
|
||||
<th>聯絡電話</th>
|
||||
<th>地址</th>
|
||||
<th>服務項目</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="vendorTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
let vendors = [
|
||||
{ id: 1, name: "好修電器", contact: "陳先生", phone: "0912-111-222", address: "台北市大安區仁愛路一段1號", service: "家電維修" },
|
||||
{ id: 2, name: "強力水電", contact: "林小姐", phone: "0988-333-444", address: "新北市板橋區文化路二段88號", service: "水電工程" },
|
||||
{ id: 3, name: "快速冷氣", contact: "張先生", phone: "0922-555-666", address: "桃園市中壢區中正路100號", service: "冷氣維修" },
|
||||
{ id: 4, name: "安全消防", contact: "李經理", phone: "0966-777-888", address: "台中市西屯區市政北七路22號", service: "消防設備" }
|
||||
];
|
||||
<nav class="pagination-fixed">
|
||||
<ul class="pagination justify-content-center" id="pagination"></ul>
|
||||
</nav>
|
||||
|
||||
function renderTable(data) {
|
||||
const tbody = document.getElementById("vendorTable");
|
||||
tbody.innerHTML = "";
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const pageData = data.slice(start, start + pageSize);
|
||||
<script>
|
||||
const pageSize = 5;
|
||||
let currentPage = 1;
|
||||
|
||||
pageData.forEach(v => {
|
||||
const row = document.createElement("tr");
|
||||
row.innerHTML = `
|
||||
<td><input type="checkbox" class="row-checkbox" data-id="${v.id}"></td>
|
||||
<td>${v.id}</td>
|
||||
<td>${v.name}</td>
|
||||
<td>${v.contact}</td>
|
||||
<td>${v.phone}</td>
|
||||
<td>${v.address || '-'}</td>
|
||||
<td>${v.service || '-'}</td>
|
||||
<td>
|
||||
<a href="repair_firm_edit.html?id=${v.id}" class="btn btn-warning btn-sm me-1 rounded-pill">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm rounded-pill" onclick="deleteVendor(${v.id})">刪除</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
let vendors = [
|
||||
{ id: 1, name: "好修電器", contact: "陳先生", phone: "0912-111-222", address: "台北市大安區仁愛路一段1號", service: "家電維修" },
|
||||
{ id: 2, name: "強力水電", contact: "林小姐", phone: "0988-333-444", address: "新北市板橋區文化路二段88號", service: "水電工程" },
|
||||
{ id: 3, name: "快速冷氣", contact: "張先生", phone: "0922-555-666", address: "桃園市中壢區中正路100號", service: "冷氣維修" },
|
||||
{ id: 4, name: "安全消防", contact: "李經理", phone: "0966-777-888", address: "台中市西屯區市政北七路22號", service: "消防設備" }
|
||||
];
|
||||
|
||||
renderPagination(data.length);
|
||||
function renderTable(data) {
|
||||
const tbody = document.getElementById("vendorTable");
|
||||
tbody.innerHTML = "";
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const pageData = data.slice(start, start + pageSize);
|
||||
|
||||
pageData.forEach(v => {
|
||||
const row = document.createElement("tr");
|
||||
row.innerHTML = `
|
||||
<td><input type="checkbox" class="row-checkbox" data-id="${v.id}"></td>
|
||||
<td>${v.id}</td>
|
||||
<td>${v.name}</td>
|
||||
<td>${v.contact}</td>
|
||||
<td>${v.phone}</td>
|
||||
<td>${v.address || '-'}</td>
|
||||
<td>${v.service || '-'}</td>
|
||||
<td>
|
||||
<a href="repair_firm_edit.html?id=${v.id}" class="btn btn-outline-secondary btn-sm">編輯</a>
|
||||
<button class="btn btn-outline-danger btn-sm" onclick="deleteVendor(${v.id})">刪除</button>
|
||||
</td>
|
||||
`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
renderPagination(data.length);
|
||||
}
|
||||
|
||||
function renderPagination(totalItems) {
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
const pagination = document.getElementById("pagination");
|
||||
pagination.innerHTML = "";
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const li = document.createElement("li");
|
||||
li.className = "page-item" + (i === currentPage ? " active" : "");
|
||||
li.innerHTML = `<a class="page-link" href="#" onclick="goToPage(${i})">${i}</a>`;
|
||||
pagination.appendChild(li);
|
||||
}
|
||||
}
|
||||
|
||||
function renderPagination(totalItems) {
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
const pagination = document.getElementById("pagination");
|
||||
pagination.innerHTML = "";
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
search();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const li = document.createElement("li");
|
||||
li.className = "page-item" + (i === currentPage ? " active" : "");
|
||||
li.innerHTML = `<a class="page-link" href="#" onclick="goToPage(${i})">${i}</a>`;
|
||||
pagination.appendChild(li);
|
||||
}
|
||||
function search() {
|
||||
const nameInput = document.getElementById("searchName").value.toLowerCase();
|
||||
const addressInput = document.getElementById("searchAddress").value.toLowerCase();
|
||||
|
||||
const filtered = vendors.filter(v =>
|
||||
v.name.toLowerCase().includes(nameInput) &&
|
||||
(v.address ? v.address.toLowerCase().includes(addressInput) : true)
|
||||
);
|
||||
|
||||
renderTable(filtered);
|
||||
}
|
||||
|
||||
function toggleAll(source) {
|
||||
const checkboxes = document.querySelectorAll(".row-checkbox");
|
||||
checkboxes.forEach(cb => cb.checked = source.checked);
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
if (!confirm("確定要刪除勾選的資料嗎?")) return;
|
||||
const selected = document.querySelectorAll(".row-checkbox:checked");
|
||||
if (selected.length === 0) {
|
||||
alert("請先勾選要刪除的資料");
|
||||
return;
|
||||
}
|
||||
const idsToDelete = Array.from(selected).map(cb => parseInt(cb.dataset.id));
|
||||
vendors = vendors.filter(v => !idsToDelete.includes(v.id));
|
||||
currentPage = 1;
|
||||
search();
|
||||
document.getElementById("selectAll").checked = false;
|
||||
}
|
||||
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
search();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
|
||||
function search() {
|
||||
const nameInput = document.getElementById("searchName").value.toLowerCase();
|
||||
const addressInput = document.getElementById("searchAddress").value.toLowerCase();
|
||||
|
||||
const filtered = vendors.filter(v =>
|
||||
v.name.toLowerCase().includes(nameInput) &&
|
||||
v.address.toLowerCase().includes(addressInput)
|
||||
);
|
||||
|
||||
renderTable(filtered);
|
||||
}
|
||||
|
||||
function toggleAll(source) {
|
||||
const checkboxes = document.querySelectorAll(".row-checkbox");
|
||||
checkboxes.forEach(cb => cb.checked = source.checked);
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
if (!confirm("確定要刪除勾選的資料嗎?")) return;
|
||||
const selected = document.querySelectorAll(".row-checkbox:checked");
|
||||
const idsToDelete = Array.from(selected).map(cb => parseInt(cb.dataset.id));
|
||||
vendors = vendors.filter(v => !idsToDelete.includes(v.id));
|
||||
function deleteVendor(id) {
|
||||
if (!confirm("確定要刪除此筆資料嗎?")) return;
|
||||
vendors = vendors.filter(v => v.id !== id);
|
||||
const totalPages = Math.ceil(vendors.length / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0) {
|
||||
currentPage = totalPages;
|
||||
} else if (vendors.length === 0) {
|
||||
currentPage = 1;
|
||||
search();
|
||||
document.getElementById("selectAll").checked = false;
|
||||
}
|
||||
search();
|
||||
}
|
||||
|
||||
function deleteVendor(id) {
|
||||
if (!confirm("確定要刪除此筆資料嗎?")) return;
|
||||
vendors = vendors.filter(v => v.id !== id);
|
||||
search();
|
||||
}
|
||||
function exportVendors() {
|
||||
alert("匯出搜尋結果(若無搜尋條件則匯出全部)");
|
||||
}
|
||||
|
||||
function exportVendors() {
|
||||
alert("匯出搜尋結果(若無搜尋條件則匯出全部)");
|
||||
}
|
||||
|
||||
search(); // 初始載入
|
||||
</script>
|
||||
search(); // 初始載入
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -15,7 +15,7 @@
|
||||
|
||||
.container.mt-4 {
|
||||
flex-grow: 1;
|
||||
padding-bottom: 70px;
|
||||
/* padding-bottom: 70px; Removed as pagination is no longer fixed */
|
||||
}
|
||||
|
||||
#qrcodeModal .modal-body {
|
||||
@ -24,32 +24,27 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
nav.pagination-fixed {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #f8f9fa;
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid #dee2e6;
|
||||
z-index: 1000;
|
||||
/* Removed nav.pagination-fixed styles */
|
||||
|
||||
/* Custom styles ONLY for buttons that are .btn but NOT .btn-sm or .btn-lg */
|
||||
/* This ensures .btn-sm (like pagination and table action buttons) use Bootstrap's default styling */
|
||||
.btn:not(.btn-sm):not(.btn-lg) {
|
||||
font-weight: 500; /* Custom font-weight for standard-sized buttons */
|
||||
font-size: 1rem; /* Custom font-size for standard-sized buttons */
|
||||
padding: 0.5rem 0.75rem; /* Custom padding for standard-sized buttons */
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
nav.pagination-fixed .pagination {
|
||||
margin-bottom: 0;
|
||||
/* Custom hover effect ONLY for buttons that are .btn but NOT .btn-sm or .btn-lg */
|
||||
.btn:not(.btn-sm):not(.btn-lg):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
/* Ensure all buttons, including btn-sm, can have rounded-pill if specified by class */
|
||||
.rounded-pill {
|
||||
border-radius: 50rem !important; /* Ensure pill shape if class is present */
|
||||
}
|
||||
|
||||
button.btn {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
button.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -73,7 +68,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<table class="table table-bordered">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll" onclick="toggleAll(this)"></th>
|
||||
@ -87,13 +82,15 @@
|
||||
</thead>
|
||||
<tbody id="residentTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav class="pagination-fixed">
|
||||
<ul class="pagination justify-content-center" id="pagination"></ul>
|
||||
</nav>
|
||||
|
||||
<div class="modal fade" id="qrcodeModal" tabindex="-1" aria-labelledby="qrcodeModalLabel" aria-hidden="true">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>第 <span id="currentPageDisplay">1</span> 頁,共 <span id="totalPagesDisplay">1</span> 頁</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary me-1" id="prevBtn">上一頁</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="nextBtn">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> <div class="modal fade" id="qrcodeModal" tabindex="-1" aria-labelledby="qrcodeModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@ -111,8 +108,10 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrcodejs/qrcode.min.js"></script>
|
||||
|
||||
<script>
|
||||
const pageSize = 10;
|
||||
const pageSize = 5;
|
||||
let currentPage = 1;
|
||||
let currentFilteredData = []; // To store the currently filtered data
|
||||
|
||||
let residents = [
|
||||
{ id: 1, name: "王小明", email: "xiaoming@example.com", phone: "0912-345-678", room: "A101" },
|
||||
{ id: 2, name: "林美麗", email: "meili@example.com", phone: "0987-654-321", room: "A102" },
|
||||
@ -128,15 +127,16 @@
|
||||
{ id: 12, name: "鄧紫棋", email: "gem@example.com", phone: "0944-444-444", room: "F602" }
|
||||
];
|
||||
|
||||
function renderTable(data) {
|
||||
function renderTable() { // Removed 'data' parameter, uses currentFilteredData
|
||||
const tbody = document.getElementById("residentTable");
|
||||
tbody.innerHTML = "";
|
||||
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const pageData = data.slice(start, start + pageSize);
|
||||
const pageData = currentFilteredData.slice(start, start + pageSize);
|
||||
|
||||
pageData.forEach(r => {
|
||||
const row = document.createElement("tr");
|
||||
// The "開通" button is .btn-sm, will use Bootstrap default small styling
|
||||
row.innerHTML = `
|
||||
<td><input type="checkbox" class="row-checkbox" data-id="${r.id}"></td>
|
||||
<td>${r.id}</td>
|
||||
@ -149,40 +149,58 @@
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
renderPagination(data.length);
|
||||
}
|
||||
|
||||
function renderPagination(totalItems) {
|
||||
// Update pagination display
|
||||
const totalItems = currentFilteredData.length;
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
const pagination = document.getElementById("pagination");
|
||||
pagination.innerHTML = "";
|
||||
document.getElementById("currentPageDisplay").textContent = currentPage;
|
||||
document.getElementById("totalPagesDisplay").textContent = totalPages > 0 ? totalPages : 1;
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const li = document.createElement("li");
|
||||
li.className = "page-item" + (i === currentPage ? " active" : "");
|
||||
li.innerHTML = `<a class="page-link" href="#" onclick="goToPage(${i})">${i}</a>`;
|
||||
pagination.appendChild(li);
|
||||
}
|
||||
document.getElementById("prevBtn").disabled = currentPage === 1;
|
||||
document.getElementById("nextBtn").disabled = currentPage === totalPages || totalPages === 0;
|
||||
}
|
||||
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
search();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
// Removed old renderPagination function
|
||||
|
||||
function search() {
|
||||
function applyFiltersAndRender() {
|
||||
const nameInput = document.getElementById("searchName").value.toLowerCase();
|
||||
const emailInput = document.getElementById("searchEmail").value.toLowerCase();
|
||||
const roomInput = document.getElementById("searchRoom").value.toLowerCase();
|
||||
|
||||
const filtered = residents.filter(r =>
|
||||
currentFilteredData = residents.filter(r =>
|
||||
r.name.toLowerCase().includes(nameInput) &&
|
||||
r.email.toLowerCase().includes(emailInput) &&
|
||||
r.room.toLowerCase().includes(roomInput)
|
||||
);
|
||||
|
||||
renderTable(filtered);
|
||||
const totalPages = Math.ceil(currentFilteredData.length / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0) {
|
||||
currentPage = totalPages;
|
||||
} else if (totalPages === 0) {
|
||||
currentPage = 1;
|
||||
}
|
||||
renderTable();
|
||||
}
|
||||
|
||||
document.getElementById("prevBtn").addEventListener("click", () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
renderTable();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("nextBtn").addEventListener("click", () => {
|
||||
const totalPages = Math.ceil(currentFilteredData.length / pageSize);
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
renderTable();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
function search() {
|
||||
currentPage = 1; // Reset to page 1 for new search
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
|
||||
function toggleAll(source) {
|
||||
@ -197,21 +215,23 @@
|
||||
const idsToDelete = Array.from(selected).map(cb => parseInt(cb.dataset.id));
|
||||
|
||||
residents = residents.filter(r => !idsToDelete.includes(r.id));
|
||||
currentPage = 1;
|
||||
search();
|
||||
// currentFilteredData will be updated by applyFiltersAndRender
|
||||
// currentPage will be adjusted if necessary by applyFiltersAndRender
|
||||
applyFiltersAndRender();
|
||||
document.getElementById("selectAll").checked = false;
|
||||
}
|
||||
|
||||
function showQRCode(url) {
|
||||
const qrContainer = document.getElementById("qrcode");
|
||||
qrContainer.innerHTML = "";
|
||||
qrContainer.innerHTML = ""; // Clear previous QR code
|
||||
new QRCode(qrContainer, {
|
||||
text: url,
|
||||
width: 200,
|
||||
width: 200, // Ensure QR code is reasonably sized
|
||||
height: 200
|
||||
});
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById("qrcodeModal"));
|
||||
const modalElement = document.getElementById("qrcodeModal");
|
||||
const modal = bootstrap.Modal.getInstance(modalElement) || new bootstrap.Modal(modalElement);
|
||||
modal.show();
|
||||
}
|
||||
|
||||
@ -219,7 +239,8 @@
|
||||
alert("這裡可以接掃描裝置或開啟相機功能");
|
||||
}
|
||||
|
||||
search(); // 初次載入
|
||||
// Initial load
|
||||
search();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -7,17 +7,29 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { display: flex; flex-direction: column; min-height: 100vh; margin: 0; }
|
||||
.container.mt-4 { flex-grow: 1; padding-bottom: 70px; }
|
||||
nav.pagination-fixed {
|
||||
position: fixed; left: 0; bottom: 0; width: 100%;
|
||||
background-color: #f8f9fa; padding: 10px 0; border-top: 1px solid #dee2e6; z-index: 1000;
|
||||
.container.mt-4 { flex-grow: 1; }
|
||||
|
||||
/* Custom styles ONLY for buttons that are .btn but NOT .btn-sm or .btn-lg */
|
||||
/* This ensures .btn-sm (like pagination and table action buttons) use Bootstrap's default styling */
|
||||
.btn:not(.btn-sm):not(.btn-lg) {
|
||||
font-weight: 500; /* Custom font-weight for standard-sized buttons */
|
||||
font-size: 1rem; /* Custom font-size for standard-sized buttons */
|
||||
padding: 0.5rem 0.75rem; /* Custom padding for standard-sized buttons */
|
||||
/* The transition below is more general than Bootstrap's default, apply specifically if needed */
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
nav.pagination-fixed .pagination { margin-bottom: 0; }
|
||||
.btn {
|
||||
font-weight: 500; font-size: 1rem;
|
||||
padding: 0.5rem 0.75rem; transition: all 0.2s ease-in-out;
|
||||
|
||||
/* Custom hover effect ONLY for buttons that are .btn but NOT .btn-sm or .btn-lg */
|
||||
.btn:not(.btn-sm):not(.btn-lg):hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.1);
|
||||
}
|
||||
.btn:hover { transform: translateY(-1px); box-shadow: 0 2px 6px rgba(0,0,0,0.1); }
|
||||
|
||||
/* Default Bootstrap .btn already includes transitions.
|
||||
If you had other general .btn styles that should apply to ALL buttons (including .btn-sm)
|
||||
and are compatible with notice_list.html's appearance, they could go here.
|
||||
For "exactly the same" pagination buttons, it's safer to let Bootstrap handle .btn-sm styling.
|
||||
*/
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -50,7 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<table class="table table-bordered">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll" onclick="toggleAll(this)"></th>
|
||||
@ -64,15 +76,19 @@
|
||||
</thead>
|
||||
<tbody id="residentTable"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<nav class="pagination-fixed">
|
||||
<ul class="pagination justify-content-center" id="pagination"></ul>
|
||||
</nav>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>第 <span id="currentPageDisplay">1</span> 頁,共 <span id="totalPagesDisplay">1</span> 頁</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary me-1" id="prevBtn">上一頁</button>
|
||||
<button class="btn btn-sm btn-outline-secondary" id="nextBtn">下一頁</button>
|
||||
</div>
|
||||
</div>
|
||||
</div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const pageSize = 10;
|
||||
const pageSize = 5;
|
||||
let currentPage = 1;
|
||||
let currentFilteredData = []; // To store the currently filtered data
|
||||
|
||||
let residents = [
|
||||
{ id: 1, name: "王小明", email: "xiaoming@example.com", phone: "0912-345-678", room: "A101" },
|
||||
@ -89,14 +105,15 @@
|
||||
{ id: 12, name: "鄧紫棋", email: "gem@example.com", phone: "0944-444-444", room: "A202" }
|
||||
];
|
||||
|
||||
function renderTable(data) {
|
||||
function renderTable() {
|
||||
const tbody = document.getElementById("residentTable");
|
||||
tbody.innerHTML = "";
|
||||
const start = (currentPage - 1) * pageSize;
|
||||
const pageData = data.slice(start, start + pageSize);
|
||||
const pageData = currentFilteredData.slice(start, start + pageSize);
|
||||
|
||||
pageData.forEach(r => {
|
||||
const row = document.createElement("tr");
|
||||
// Table action buttons are also .btn-sm and will use Bootstrap defaults
|
||||
row.innerHTML = `
|
||||
<td><input type="checkbox" class="row-checkbox" data-id="${r.id}"></td>
|
||||
<td>${r.id}</td>
|
||||
@ -112,38 +129,54 @@
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
renderPagination(data.length);
|
||||
}
|
||||
|
||||
function renderPagination(totalItems) {
|
||||
const totalItems = currentFilteredData.length;
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
const pagination = document.getElementById("pagination");
|
||||
pagination.innerHTML = "";
|
||||
document.getElementById("currentPageDisplay").textContent = currentPage;
|
||||
document.getElementById("totalPagesDisplay").textContent = totalPages > 0 ? totalPages : 1;
|
||||
|
||||
for (let i = 1; i <= totalPages; i++) {
|
||||
const li = document.createElement("li");
|
||||
li.className = "page-item" + (i === currentPage ? " active" : "");
|
||||
li.innerHTML = `<a class="page-link" href="#" onclick="goToPage(${i})">${i}</a>`;
|
||||
pagination.appendChild(li);
|
||||
document.getElementById("prevBtn").disabled = currentPage === 1;
|
||||
document.getElementById("nextBtn").disabled = currentPage === totalPages || totalPages === 0;
|
||||
}
|
||||
|
||||
function applyFiltersAndRender() {
|
||||
const nameInput = document.getElementById("searchName").value.toLowerCase();
|
||||
const emailInput = document.getElementById("searchEmail").value.toLowerCase();
|
||||
|
||||
currentFilteredData = residents.filter(r =>
|
||||
r.name.toLowerCase().includes(nameInput) &&
|
||||
r.email.toLowerCase().includes(emailInput)
|
||||
);
|
||||
|
||||
const totalPages = Math.ceil(currentFilteredData.length / pageSize);
|
||||
if (currentPage > totalPages && totalPages > 0) {
|
||||
currentPage = totalPages;
|
||||
} else if (totalPages === 0) {
|
||||
currentPage = 1;
|
||||
}
|
||||
renderTable();
|
||||
}
|
||||
|
||||
|
||||
document.getElementById("prevBtn").addEventListener("click", () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
renderTable();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function goToPage(page) {
|
||||
currentPage = page;
|
||||
search();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
document.getElementById("nextBtn").addEventListener("click", () => {
|
||||
const totalPages = Math.ceil(currentFilteredData.length / pageSize);
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
renderTable();
|
||||
window.scrollTo(0, 0);
|
||||
}
|
||||
});
|
||||
|
||||
function search() {
|
||||
const nameInput = document.getElementById("searchName").value.toLowerCase();
|
||||
const emailInput = document.getElementById("searchEmail").value.toLowerCase();
|
||||
|
||||
const filtered = residents.filter(r =>
|
||||
r.name.toLowerCase().includes(nameInput) &&
|
||||
r.email.toLowerCase().includes(emailInput)
|
||||
);
|
||||
|
||||
renderTable(filtered);
|
||||
currentPage = 1;
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
|
||||
function toggleAll(source) {
|
||||
@ -156,22 +189,26 @@
|
||||
const selected = document.querySelectorAll(".row-checkbox:checked");
|
||||
const idsToDelete = Array.from(selected).map(cb => parseInt(cb.dataset.id));
|
||||
residents = residents.filter(r => !idsToDelete.includes(r.id));
|
||||
currentPage = 1;
|
||||
search();
|
||||
applyFiltersAndRender();
|
||||
document.getElementById("selectAll").checked = false;
|
||||
}
|
||||
|
||||
function deleteResident(id) {
|
||||
if (!confirm("確定要刪除此筆資料嗎?")) return;
|
||||
residents = residents.filter(r => r.id !== id);
|
||||
search();
|
||||
applyFiltersAndRender();
|
||||
}
|
||||
|
||||
function exportResidents() {
|
||||
alert("直接匯出搜尋查出的結果,如果沒有搜尋就全部匯出");
|
||||
if (currentFilteredData.length === 0) {
|
||||
console.log("Exporting all residents:", residents);
|
||||
} else {
|
||||
console.log("Exporting filtered residents:", currentFilteredData);
|
||||
}
|
||||
}
|
||||
|
||||
search(); // 初始化載入
|
||||
search();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -57,22 +57,30 @@
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.has-submenu > .submenu-toggle {
|
||||
.has-submenu > .submenu-toggle,
|
||||
.li-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.has-submenu > .submenu-toggle > span:first-child {
|
||||
.submenu-toggle > span:first-child,
|
||||
.li-content > span:first-child {
|
||||
flex-grow: 1;
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.parent-badge {
|
||||
.parent-badge,
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
padding: 2px 6px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
@ -80,7 +88,12 @@
|
||||
background-color: red;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
line-height: 1;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.parent-badge {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -112,42 +125,6 @@
|
||||
.has-submenu ul li:hover {
|
||||
background-color: #deeaf6;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
padding: 2px 6px;
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
background-color: red;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar ul li .li-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ---- 讓 "未開通居民" 等文字完整顯示 ---- */
|
||||
.sidebar ul li .li-content > span:first-child {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.sidebar ul li .li-content span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -172,6 +149,7 @@
|
||||
<li onclick="requestNavigation('resident_list.html', this)">居民列表</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="has-submenu">
|
||||
<div class="submenu-toggle" onclick="toggleSubmenu(this)">
|
||||
<span>警急通報</span>
|
||||
@ -196,19 +174,22 @@
|
||||
<span class="submenu-arrow">▼</span>
|
||||
</div>
|
||||
<ul>
|
||||
<li onclick="requestNavigation('CCCCC.html', this)">首頁輪播廣告</li>
|
||||
<li onclick="requestNavigation('CCCCC.html', this)">跑馬登廣告</li>
|
||||
<li onclick="requestNavigation('carousel_ads_list.html', this)">首頁輪播廣告</li>
|
||||
<li onclick="requestNavigation('marquee_ads_list.html', this)">跑馬登廣告</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li onclick="requestNavigation('some_page1.html', this)">出入管理</li>
|
||||
|
||||
<li onclick="requestNavigation('message_list.html', this)">
|
||||
<div class="li-content">
|
||||
<span>訊息通知</span>
|
||||
<span class="badge" data-count="99">99</span>
|
||||
<span class="submenu-arrow"> </span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="has-submenu">
|
||||
|
||||
<li class="has-submenu">
|
||||
<div class="submenu-toggle" onclick="toggleSubmenu(this)">
|
||||
<span>水電報修</span>
|
||||
<span class="parent-badge"></span>
|
||||
@ -216,41 +197,46 @@
|
||||
</div>
|
||||
<ul>
|
||||
<li onclick="requestNavigation('repair-list.html', this)">
|
||||
<div class="li-content">
|
||||
<div class="li-content">
|
||||
<span>報修申請</span>
|
||||
<span class="badge" data-count="99">99</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li onclick="requestNavigation('repair_firm_list.html', this)">報修廠商</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li onclick="requestNavigation('feedback_list.html', this)">
|
||||
<div class="li-content">
|
||||
<span>意見回饋</span>
|
||||
<span class="badge" data-count="99">99</span>
|
||||
<span class="submenu-arrow"> </span>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li onclick="requestNavigation('some_page6.html', this)">報表匯出</li>
|
||||
<li onclick="requestNavigation('some_page7.html', this)">設定</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
function requestNavigation(url, clickedElement) {
|
||||
console.log('Sidebar: 發送導航請求 ->', url);
|
||||
parent.postMessage({ type: 'navigate', url: url }, '*');
|
||||
|
||||
// 移除所有主項目和子項目的 active class
|
||||
const allItems = document.querySelectorAll('.sidebar ul li, .sidebar ul ul li');
|
||||
allItems.forEach(item => item.classList.remove('active'));
|
||||
|
||||
// 為當前點擊的項目添加 active class
|
||||
clickedElement.classList.add('active');
|
||||
|
||||
// 處理父層active標示(僅當點擊子選單時)
|
||||
if (clickedElement.closest('.has-submenu ul')) {
|
||||
const parentLi = clickedElement.closest('.has-submenu');
|
||||
if (parentLi) {
|
||||
// 移除所有頂層 active,改由父層設定 active
|
||||
document.querySelectorAll('.sidebar > ul > li').forEach(li => li.classList.remove('active'));
|
||||
parentLi.classList.add('active');
|
||||
}
|
||||
} else {
|
||||
// 點擊頂層 li
|
||||
document.querySelectorAll('.sidebar > ul > li').forEach(li => li.classList.remove('active'));
|
||||
clickedElement.classList.add('active');
|
||||
}
|
||||
@ -274,21 +260,18 @@
|
||||
submenuParents.forEach(parent => {
|
||||
const isOpen = parent.classList.contains('open');
|
||||
const badgeContainer = parent.querySelector('.parent-badge');
|
||||
|
||||
const subBadges = parent.querySelectorAll('ul .badge');
|
||||
let total = 0;
|
||||
|
||||
let total = 0;
|
||||
subBadges.forEach(badge => {
|
||||
const raw = badge.getAttribute('data-count');
|
||||
const val = parseInt(raw, 10);
|
||||
if (!isNaN(val)) {
|
||||
total += val;
|
||||
}
|
||||
if (!isNaN(val)) total += val;
|
||||
});
|
||||
|
||||
if (!isOpen && total > 0) {
|
||||
badgeContainer.textContent = total;
|
||||
badgeContainer.style.display = 'inline-block';
|
||||
badgeContainer.style.display = 'inline-flex';
|
||||
} else {
|
||||
badgeContainer.style.display = 'none';
|
||||
badgeContainer.textContent = '';
|
||||
@ -296,7 +279,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
updateParentBadges();
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user