REMOVE LDAP
This commit is contained in:
12
templates/403.html
Normal file
12
templates/403.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}權限不足{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container text-center py-5">
|
||||
<h1 class="display-1">403</h1>
|
||||
<h2 class="mb-4">權限不足 (Forbidden)</h2>
|
||||
<p class="lead">抱歉,您沒有權限存取此頁面。</p>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-primary mt-3">返回總表</a>
|
||||
</div>
|
||||
{% endblock %}
|
12
templates/404.html
Normal file
12
templates/404.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}找不到頁面{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container text-center py-5">
|
||||
<h1 class="display-1">404</h1>
|
||||
<h2 class="mb-4">找不到頁面 (Not Found)</h2>
|
||||
<p class="lead">抱歉,您要找的頁面不存在。</p>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-primary mt-3">返回總表</a>
|
||||
</div>
|
||||
{% endblock %}
|
41
templates/activate_spec.html
Normal file
41
templates/activate_spec.html
Normal file
@@ -0,0 +1,41 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}啟用暫時規範{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="mb-4">上傳簽核檔案以啟用規範</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
規範編號: <strong>{{ spec.spec_code }}</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<p><strong>主題:</strong> {{ spec.title }}</p>
|
||||
<div class="alert alert-info">
|
||||
請上傳已經過完整簽核的 PDF 檔案。上傳後,此規範的狀態將變為「生效」。
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="signed_file" class="form-label"><strong>已簽核的 PDF 檔案</strong></label>
|
||||
<input class="form-control" type="file" id="signed_file" name="signed_file" accept=".pdf" required>
|
||||
</div>
|
||||
|
||||
<!-- 郵件通知對象選擇 -->
|
||||
<div class="mb-3">
|
||||
<label for="recipients" class="form-label"><strong>郵件通知對象</strong></label>
|
||||
<textarea class="form-control" id="recipients" name="recipients" rows="3" placeholder="mail1@example.com; mail2@example.com">{{ saved_emails or '' }}</textarea>
|
||||
<div class="form-text">請輸入完整郵件地址,多筆請以分號 (;) 分隔。</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-success">上傳並啟用</button>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-secondary">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// 郵件通知輸入改為手動維護,以分號分隔多筆郵件。
|
||||
</script>
|
||||
{% endblock %}
|
98
templates/base.html
Normal file
98
templates/base.html
Normal file
@@ -0,0 +1,98 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-Hant">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}暫時規範系統{% endblock %}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||
<!-- Toast UI Editor Core CSS -->
|
||||
<link rel="stylesheet" href="https://uicdn.toast.com/editor/latest/toastui-editor.min.css" />
|
||||
<!-- Plugins CSS -->
|
||||
<link rel="stylesheet" href="https://uicdn.toast.com/tui-color-picker/latest/tui-color-picker.min.css" />
|
||||
<link rel="stylesheet" href="https://uicdn.toast.com/editor-plugin-color-syntax/latest/toastui-editor-plugin-color-syntax.min.css" />
|
||||
<link rel="stylesheet" href="https://uicdn.toast.com/editor-plugin-table-merged-cell/latest/toastui-editor-plugin-table-merged-cell.min.css" />
|
||||
<link rel="stylesheet" href="https://uicdn.toast.com/tui-image-editor/latest/tui-image-editor.min.css">
|
||||
<!-- Tom Select CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/css/tom-select.bootstrap5.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="{{ url_for('temp_spec.spec_list') }}">暫時規範系統</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
{% if current_user.is_authenticated %}
|
||||
{% if current_user.role in ['editor', 'admin'] %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('temp_spec.create_temp_spec') }}">暫時規範建立</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('temp_spec.spec_list') }}">總表檢視</a>
|
||||
</li>
|
||||
{% if current_user.role == 'admin' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('admin.user_list') }}">權限管理</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('auth.logout') }}">登出</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('auth.login') }}">登入</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container mt-4">
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<!-- Toast 容器 -->
|
||||
<div class="toast-container position-fixed bottom-0 end-0 p-3">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header">
|
||||
<i class="bi bi-bell-fill me-2"></i>
|
||||
<strong class="me-auto">通知</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<!-- Toast UI Editor Dependencies & Core -->
|
||||
<script src="https://uicdn.toast.com/tui-color-picker/latest/tui-color-picker.min.js"></script>
|
||||
<script src="https://uicdn.toast.com/editor/latest/toastui-editor-all.min.js"></script>
|
||||
<!-- Plugins JS -->
|
||||
<script src="https://uicdn.toast.com/editor-plugin-color-syntax/latest/toastui-editor-plugin-color-syntax.min.js"></script>
|
||||
<script src="https://uicdn.toast.com/editor-plugin-table-merged-cell/latest/toastui-editor-plugin-table-merged-cell.min.js"></script>
|
||||
<!-- Tom Select JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.3.1/dist/js/tom-select.complete.min.js"></script>
|
||||
|
||||
<script>
|
||||
// 啟用所有 Toast
|
||||
const toastElList = document.querySelectorAll('.toast');
|
||||
const toastList = [...toastElList].map(toastEl => new bootstrap.Toast(toastEl).show());
|
||||
</script>
|
||||
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
83
templates/create_temp_spec_form.html
Normal file
83
templates/create_temp_spec_form.html
Normal file
@@ -0,0 +1,83 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}建立新的暫時規範{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-10">
|
||||
<h2 class="mb-4">建立新的暫時規範</h2>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form id="spec-form" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="theme" class="form-label">主題/目的</label>
|
||||
<input type="text" class="form-control" id="theme" name="theme" required>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="applicant" class="form-label">申請者</label>
|
||||
<input type="text" class="form-control" id="applicant" name="applicant" value="{{ current_user.name }}" readonly>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<label for="applicant_phone" class="form-label">電話(分機)</label>
|
||||
<input type="text" class="form-control" id="applicant_phone" name="applicant_phone">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">站別 (可多選)</label>
|
||||
<div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="probing"><label>點測</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="dicing"><label>切割</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="diebond"><label>晶粒黏著</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="wirebond"><label>銲線黏著</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="solder"><label>錫膏焊接</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="molding"><label>成型</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="degate"><label>去膠</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="deflash"><label>吹砂</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="plating"><label>電鍍</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="trimform"><label>切彎腳</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="marking"><label>印字</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="tmtt"><label>測試</label></div>
|
||||
<div class="form-check form-check-inline"><input class="form-check-input" type="checkbox" name="station" value="other"><label>其他</label></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label">TCCS Level</label>
|
||||
<div class="input-group">
|
||||
<select class="form-select" name="tccs_level">
|
||||
<option selected value="">請選擇 Level...</option>
|
||||
<option value="l1">Level 1</option>
|
||||
<option value="l2">Level 2</option>
|
||||
<option value="l3">Level 3</option>
|
||||
<option value="l4">Level 4</option>
|
||||
</select>
|
||||
<div class="input-group-text">
|
||||
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="man"><label>人</label></div>
|
||||
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="machine"><label>機</label></div>
|
||||
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="material"><label>料</label></div>
|
||||
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="method"><label>法</label></div>
|
||||
<div class="form-check form-check-inline mb-0"><input class="form-check-input" type="radio" name="tccs_4m" value="env"><label>環</label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3"><label for="package" class="form-label">Package</label><input type="text" class="form-control" id="package" name="package"></div>
|
||||
<div class="col-md-4 mb-3"><label for="lot_number" class="form-label">工單批號</label><input type="text" class="form-control" id="lot_number" name="lot_number"></div>
|
||||
<div class="col-md-4 mb-3"><label for="equipment_type" class="form-label">設備型(編)號</label><input type="text" class="form-control" id="equipment_type" name="equipment_type"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end">
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-secondary me-2">取消</a>
|
||||
<button type="submit" class="btn btn-primary">建立並開始編輯</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
59
templates/extend_spec.html
Normal file
59
templates/extend_spec.html
Normal file
@@ -0,0 +1,59 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}展延暫時規範{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="mb-4">展延暫時規範</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
規範編號: <strong>{{ spec.spec_code }}</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<p><strong>主題:</strong> {{ spec.title }}</p>
|
||||
<p><strong>原結束日期:</strong> {{ spec.end_date|taiwan_date }}</p>
|
||||
<p><strong>展延次數:</strong>
|
||||
<span class="badge bg-info">{{ spec.extension_count }} / 2</span>
|
||||
<small class="text-muted ms-2">
|
||||
剩餘可展延次數: <strong>{{ 2 - spec.extension_count }}</strong> 次
|
||||
</small>
|
||||
</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="new_end_date" class="form-label"><strong>新的結束日期</strong></label>
|
||||
<input type="date" class="form-control" id="new_end_date" name="new_end_date"
|
||||
value="{{ default_new_end_date|taiwan_date }}" required>
|
||||
<div class="form-text">請輸入完整郵件地址,多筆請以分號 (;) 分隔。</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="new_file" class="form-label"><strong>重新上傳佐證檔案 (必填)</strong></label>
|
||||
<input class="form-control" type="file" id="new_file" name="new_file" accept=".pdf" required>
|
||||
<div class="form-text">請上傳展延申請的相關佐證文件 (PDF 格式)。</div>
|
||||
</div>
|
||||
|
||||
<!-- 郵件通知對象選擇 -->
|
||||
<div class="mb-3">
|
||||
<label for="recipients" class="form-label"><strong>郵件通知對象</strong></label>
|
||||
{% if saved_emails %}
|
||||
<div class="alert alert-info mb-2">
|
||||
<small>以下為先前儲存的通知清單,可直接調整或重新輸入。</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
<textarea class="form-control" id="recipients" name="recipients" rows="3" placeholder="mail1@example.com; mail2@example.com">{{ saved_emails or '' }}</textarea>
|
||||
<div class="form-text">請輸入完整郵件地址,多筆請以分號 (;) 分隔。</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">確認展延</button>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-secondary">取消</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// 郵件通知輸入改為分號分隔字串,無需額外初始化。
|
||||
</script>
|
||||
{% endblock %}
|
43
templates/login.html
Normal file
43
templates/login.html
Normal file
@@ -0,0 +1,43 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}登入 - 暫規系統{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<h2 class="text-center mb-4">登入</h2>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category or 'danger' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">帳號</label>
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
placeholder="請輸入帳號" value="{{ username or '' }}" required>
|
||||
<div class="form-text text-light fw-bold">帳號可使用公司信箱或指定字串。</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">密碼</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<button type="submit" class="btn btn-primary">登入</button>
|
||||
<a href="{{ url_for('auth.register') }}" class="btn btn-link">建立新帳號</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
39
templates/onlyoffice_editor.html
Normal file
39
templates/onlyoffice_editor.html
Normal file
@@ -0,0 +1,39 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}編輯規範 - {{ spec.spec_code }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div>
|
||||
<h2 class="mb-0">正在編輯: {{ spec.spec_code }}</h2>
|
||||
<p class="lead text-muted">主題: {{ spec.title }}</p>
|
||||
</div>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-secondary"><i class="bi bi-arrow-left-circle me-2"></i>返回總表</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body p-0" style="height: 85vh;">
|
||||
<div id="placeholder"></div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="{{ onlyoffice_url }}web-apps/apps/api/documents/api.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 從後端接收的設定
|
||||
const config = {{ config|tojson|safe }};
|
||||
|
||||
// 建立 DocEditor 物件
|
||||
const docEditor = new DocsAPI.DocEditor("placeholder", config);
|
||||
|
||||
// 您可以在這裡加入更多事件處理,例如:
|
||||
// config.events = {
|
||||
// 'onAppReady': function() { console.log('Editor is ready'); },
|
||||
// 'onDocumentStateChange': function(event) { console.log('Document state changed'); },
|
||||
// };
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
53
templates/register.html
Normal file
53
templates/register.html
Normal file
@@ -0,0 +1,53 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}註冊帳號 - 暫規系統{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6 col-lg-5">
|
||||
<h2 class="text-center mb-4">建立新帳號</h2>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category or 'danger' }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">帳號</label>
|
||||
<input type="text" class="form-control" id="username" name="username"
|
||||
placeholder="請輸入帳號" value="{{ username or '' }}" required>
|
||||
<div class="form-text">建議以公司信箱為帳號,避免重複。</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" id="name" name="name"
|
||||
placeholder="請輸入姓名" value="{{ name or '' }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">密碼</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
<div class="form-text">密碼需至少 6 碼。</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="confirm_password" class="form-label">確認密碼</label>
|
||||
<input type="password" class="form-control" id="confirm_password" name="confirm_password" required>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<button type="submit" class="btn btn-success">註冊</button>
|
||||
<a href="{{ url_for('auth.login') }}" class="btn btn-link">回登入頁</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
34
templates/spec_history.html
Normal file
34
templates/spec_history.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}操作歷史 - {{ spec.spec_code }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<div>
|
||||
<h2 class="mb-0">操作歷史紀錄</h2>
|
||||
<p class="lead text-muted">規範編號: {{ spec.spec_code }}</p>
|
||||
</div>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-secondary"><i class="bi bi-arrow-left-circle me-2"></i>返回總表</a>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for entry in history %}
|
||||
<li class="list-group-item">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">
|
||||
<span class="badge bg-primary rounded-pill me-2">{{ entry.action }}</span>
|
||||
由 <strong>{{ entry.user.name if entry.user and entry.user.name else (entry.user.username if entry.user else '[已刪除的使用者]') }}</strong> 執行
|
||||
</h5>
|
||||
<small>{{ entry.timestamp|taiwan_time }}</small>
|
||||
</div>
|
||||
<p class="mb-1 mt-2">{{ entry.details }}</p>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="list-group-item">沒有任何歷史紀錄。</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
169
templates/spec_list.html
Normal file
169
templates/spec_list.html
Normal file
@@ -0,0 +1,169 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}臨時規格清單{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2 class="mb-0">臨時規格清單</h2>
|
||||
{% if current_user.role in ['editor','admin'] %}
|
||||
<a href="{{ url_for('temp_spec.create_temp_spec') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle-fill me-2"></i>暫時規範建立
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<form method="get" action="{{ url_for('temp_spec.spec_list') }}" class="row g-3 align-items-center">
|
||||
<div class="col-md-6">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
||||
<input type="text" name="query" class="form-control" placeholder="依規格編號或標題搜尋..." value="{{ query or '' }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<select name="status" class="form-select">
|
||||
<option value="">所有狀態</option>
|
||||
<option value="pending_approval" {% if status == 'pending_approval' %}selected{% endif %}>待核准</option>
|
||||
<option value="active" {% if status == 'active' %}selected{% endif %}>生效中</option>
|
||||
<option value="terminated" {% if status == 'terminated' %}selected{% endif %}>已終止</option>
|
||||
<option value="expired" {% if status == 'expired' %}selected{% endif %}>已過期</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary w-100">篩選</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover table-striped align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>規格編號</th>
|
||||
<th>標題</th>
|
||||
<th>申請人</th>
|
||||
<th>建立日期</th>
|
||||
<th>結束日期</th>
|
||||
<th class="text-center">剩餘天數</th>
|
||||
<th class="text-center">狀態</th>
|
||||
<th class="text-center">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for spec in specs %}
|
||||
<tr>
|
||||
<td>{{ spec.spec_code }}</td>
|
||||
<td>{{ spec.title }}</td>
|
||||
<td>{{ spec.applicant }}</td>
|
||||
<td>{{ spec.created_at|taiwan_date }}</td>
|
||||
<td>{{ spec.end_date|taiwan_date }}</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if spec.status in ['active', 'expired'] %}
|
||||
{% set remaining_days = (spec.end_date - today).days %}
|
||||
{% if remaining_days < 0 %}
|
||||
{% set color_class = 'days-expired' %}
|
||||
{% elif remaining_days <= 3 %}
|
||||
{% set color_class = 'days-critical' %}
|
||||
{% elif remaining_days <= 7 %}
|
||||
{% set color_class = 'days-warning' %}
|
||||
{% else %}
|
||||
{% set color_class = 'days-safe' %}
|
||||
{% endif %}
|
||||
<span class="days-badge {{ color_class }}">
|
||||
{{ remaining_days if remaining_days >= 0 else '已過期' }}
|
||||
</span>
|
||||
{% else %}
|
||||
<span>-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if spec.status == 'active' %}
|
||||
<span class="badge fs-6 bg-success bg-opacity-75"><i class="bi bi-check-circle-fill me-1"></i>生效中</span>
|
||||
{% elif spec.status == 'pending_approval' %}
|
||||
<span class="badge fs-6 bg-info bg-opacity-75 text-dark"><i class="bi bi-hourglass-split me-1"></i>待核准</span>
|
||||
{% elif spec.status == 'terminated' %}
|
||||
<span class="badge fs-6 bg-warning bg-opacity-75 text-dark"><i class="bi bi-slash-circle-fill me-1"></i>已終止</span>
|
||||
{% else %}
|
||||
<span class="badge fs-6 bg-secondary bg-opacity-75"><i class="bi bi-calendar-x-fill me-1"></i>已過期</span>
|
||||
{% endif %}
|
||||
|
||||
{% if spec.extension_count > 0 %}
|
||||
<br>
|
||||
<div class="mt-1">
|
||||
<span class="badge bg-light text-dark border">
|
||||
<i class="bi bi-arrow-repeat me-1"></i>已延期 {{ spec.extension_count }} 次
|
||||
</span>
|
||||
{% if spec.extension_count >= 2 %}
|
||||
<br><span class="badge bg-danger mt-1">已達延期上限</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
{% if spec.status == 'pending_approval' and current_user.role in ['editor', 'admin'] %}
|
||||
<a href="{{ url_for('temp_spec.edit_spec', spec_id=spec.id) }}" class="btn btn-sm btn-warning" title="編輯"><i class="bi bi-pencil-fill"></i></a>
|
||||
{% endif %}
|
||||
|
||||
{% if current_user.role == 'admin' and spec.status == 'pending_approval' %}
|
||||
<a href="{{ url_for('temp_spec.activate_spec', spec_id=spec.id) }}" class="btn btn-sm btn-primary" title="核准"><i class="bi bi-check2-circle"></i></a>
|
||||
{% endif %}
|
||||
|
||||
{% if current_user.role in ['editor', 'admin'] and spec.status == 'active' %}
|
||||
{% if spec.extension_count < 2 %}
|
||||
<a href="{{ url_for('temp_spec.extend_spec', spec_id=spec.id) }}" class="btn btn-sm btn-secondary" title="延期"><i class="bi bi-calendar-plus"></i></a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-secondary" disabled title="已達延期上限(2次)"><i class="bi bi-calendar-x"></i></button>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('temp_spec.terminate_spec', spec_id=spec.id) }}" class="btn btn-sm btn-danger" title="終止"><i class="bi bi-x-circle"></i></a>
|
||||
{% endif %}
|
||||
|
||||
{% if current_user.role == 'admin' %}
|
||||
<form action="{{ url_for('temp_spec.delete_spec', spec_id=spec.id) }}" method="post" class="d-inline" onsubmit="return confirm('確定要刪除此規格和相關檔案嗎?此操作無法復原。');">
|
||||
<button type="submit" class="btn btn-sm btn-danger" title="刪除"><i class="bi bi-trash-fill"></i></button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% if spec.status == 'pending_approval' %}
|
||||
{% if current_user.role in ['editor', 'admin'] %}
|
||||
<a href="{{ url_for('temp_spec.download_initial_word', spec_id=spec.id) }}" class="btn btn-sm btn-primary" title="下載Word文件"><i class="bi bi-file-earmark-word-fill"></i></a>
|
||||
{% endif %}
|
||||
{% elif spec.uploads %}
|
||||
<a href="{{ url_for('temp_spec.download_signed_pdf', spec_id=spec.id) }}" class="btn btn-sm btn-success" title="下載已簽核PDF"><i class="bi bi-file-earmark-check-fill"></i></a>
|
||||
{% endif %}
|
||||
<a href="{{ url_for('temp_spec.spec_history', spec_id=spec.id) }}" class="btn btn-sm btn-outline-secondary" title="歷史記錄"><i class="bi bi-clock-history"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<nav aria-label="Page navigation">
|
||||
<ul class="pagination justify-content-center mb-0">
|
||||
<li class="page-item {% if not pagination.has_prev %}disabled{% endif %}">
|
||||
<a class="page-link" href="{{ url_for('temp_spec.spec_list', page=pagination.prev_num, query=query, status=status) }}">上一頁</a>
|
||||
</li>
|
||||
{% for page_num in pagination.iter_pages() %}
|
||||
{% if page_num %}
|
||||
<li class="page-item {% if page_num == pagination.page %}active{% endif %}"><a class="page-link" href="{{ url_for('temp_spec.spec_list', page=page_num, query=query, status=status) }}">{{ page_num }}</a></li>
|
||||
{% else %}
|
||||
<li class="page-item disabled"><span class="page-link">...</span></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
|
||||
<a class="page-link" href="{{ url_for('temp_spec.spec_list', page=pagination.next_num, query=query, status=status) }}">下一頁</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
46
templates/terminate_spec.html
Normal file
46
templates/terminate_spec.html
Normal file
@@ -0,0 +1,46 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}?蝯??急?閬?{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="mb-4">?蝯??急?閬?</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
閬?蝺刻?: <strong>{{ spec.spec_code }}</strong>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post">
|
||||
<p><strong>銝駁?:</strong> {{ spec.title }}</p>
|
||||
<div class="alert alert-warning">
|
||||
?瑁?甇斗?雿????喟?甇a遢?急?閬?嚗???霈?歇蝯迫??蝯??交???啁隞予??
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="reason" class="form-label"><strong>?蝯???</strong></label>
|
||||
<textarea class="form-control" id="reason" name="reason" rows="4" required></textarea>
|
||||
</div>
|
||||
|
||||
<!-- ?萎辣?撠情?豢? -->
|
||||
<div class="mb-3">
|
||||
<label for="recipients" class="form-label"><strong>?萎辣?撠情</strong></label>
|
||||
{% if saved_emails %}
|
||||
<div class="alert alert-info mb-2">
|
||||
<small>以下為先前儲存的通知清單,可直接調整或重新輸入。</small>
|
||||
</div>
|
||||
{% endif %}
|
||||
<textarea class="form-control" id="recipients" name="recipients" rows="3" placeholder="mail1@example.com; mail2@example.com">{{ saved_emails or '' }}</textarea>
|
||||
<div class="form-text">請輸入完整郵件地址,多筆請以分號 (;) 分隔。</div>
|
||||
|
||||
<button type="submit" class="btn btn-danger">蝣箄?蝯迫</button>
|
||||
<a href="{{ url_for('temp_spec.spec_list') }}" class="btn btn-secondary">??</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
// ?萎辣??寧??頛詨?????隞嗅?銝脯?
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
172
templates/user_management.html
Normal file
172
templates/user_management.html
Normal file
@@ -0,0 +1,172 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}帳號管理{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="mb-4">帳號管理</h2>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<i class="bi bi-person-plus-fill"></i> 新增帳號
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="{{ url_for('admin.create_user') }}" method="post" class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<label for="new-username" class="form-label">帳號</label>
|
||||
<input type="text" class="form-control" id="new-username" name="username" placeholder="例如:user@example.com" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="new-name" class="form-label">姓名</label>
|
||||
<input type="text" class="form-control" id="new-name" name="name" required>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label for="new-password" class="form-label">密碼</label>
|
||||
<input type="password" class="form-control" id="new-password" name="password" minlength="6" required>
|
||||
<div class="form-text">至少 6 碼。</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label for="new-role" class="form-label">角色</label>
|
||||
<select class="form-select" id="new-role" name="role">
|
||||
<option value="viewer" selected>檢視 (Viewer)</option>
|
||||
<option value="editor">編輯 (Editor)</option>
|
||||
<option value="admin">管理 (Admin)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-12 text-end">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="bi bi-check-lg"></i> 建立帳號
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary text-white">
|
||||
<i class="bi bi-people-fill"></i> 現有帳號
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if users %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>帳號</th>
|
||||
<th>姓名</th>
|
||||
<th>角色</th>
|
||||
<th>上次登入</th>
|
||||
<th>重設密碼</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr {% if user.id == current_user.id %}class="table-warning"{% endif %}>
|
||||
<td>{{ user.id }}</td>
|
||||
<td>
|
||||
{{ user.username }}
|
||||
{% if user.id == current_user.id %}
|
||||
<span class="badge bg-info ms-1">目前使用者</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" class="form-control form-control-sm" name="name" value="{{ user.name }}" form="update-user-{{ user.id }}" required>
|
||||
</td>
|
||||
<td>
|
||||
<select name="role" class="form-select form-select-sm" form="update-user-{{ user.id }}">
|
||||
<option value="viewer" {% if user.role == 'viewer' %}selected{% endif %}>檢視</option>
|
||||
<option value="editor" {% if user.role == 'editor' %}selected{% endif %}>編輯</option>
|
||||
<option value="admin" {% if user.role == 'admin' %}selected{% endif %}>管理</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
{% if user.last_login %}
|
||||
{{ user.last_login|taiwan_time('%Y-%m-%d %H:%M') }}
|
||||
{% else %}
|
||||
<span class="text-muted">從未登入</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<input type="password" class="form-control form-control-sm" name="password" placeholder="留空不變" form="update-user-{{ user.id }}" minlength="6">
|
||||
</td>
|
||||
<td class="text-nowrap">
|
||||
<form id="update-user-{{ user.id }}" action="{{ url_for('admin.update_user', user_id=user.id) }}" method="post" class="d-inline"></form>
|
||||
<button type="submit" class="btn btn-outline-primary btn-sm me-1" form="update-user-{{ user.id }}">
|
||||
<i class="bi bi-save"></i> 儲存
|
||||
</button>
|
||||
{% if user.id != current_user.id %}
|
||||
<form action="{{ url_for('admin.delete_user', user_id=user.id) }}" method="post" class="d-inline" onsubmit="return confirm('確定要刪除 {{ user.username }} 嗎?此動作無法復原。');">
|
||||
<button type="submit" class="btn btn-outline-danger btn-sm">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" disabled title="無法刪除自己的帳號">
|
||||
<i class="bi bi-shield-lock"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> 目前尚無帳號紀錄。
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mt-4">
|
||||
<div class="card-header">
|
||||
<i class="bi bi-info-circle"></i> 角色說明
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h6><span class="badge bg-secondary"><i class="bi bi-eye-fill"></i> 檢視 (Viewer)</span></h6>
|
||||
<ul class="small mb-0">
|
||||
<li>登入系統</li>
|
||||
<li>檢視暫規清單</li>
|
||||
<li>下載已核准的 PDF 檔案</li>
|
||||
<li>查看歷史紀錄</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6><span class="badge bg-warning text-dark"><i class="bi bi-pencil-fill"></i> 編輯 (Editor)</span></h6>
|
||||
<ul class="small mb-0">
|
||||
<li>包含 Viewer 權限</li>
|
||||
<li>建立暫規申請</li>
|
||||
<li>編輯暫規內容</li>
|
||||
<li>展延與終止暫規</li>
|
||||
<li>下載 Word 編輯檔</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h6><span class="badge bg-danger"><i class="bi bi-shield-fill"></i> 管理 (Admin)</span></h6>
|
||||
<ul class="small mb-0">
|
||||
<li>包含 Editor 權限</li>
|
||||
<li>核准待審暫規</li>
|
||||
<li>管理帳號與角色</li>
|
||||
<li>刪除暫規</li>
|
||||
<li>系統設定維護</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user