修正
This commit is contained in:
@@ -2,9 +2,11 @@ from dotenv import load_dotenv
|
|||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from fastapi.responses import JSONResponse, RedirectResponse
|
from fastapi.responses import JSONResponse, RedirectResponse, Response
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
|
from starlette.datastructures import MutableHeaders
|
||||||
|
from starlette.types import ASGIApp, Receive, Scope, Send
|
||||||
|
|
||||||
# FastAPI アプリケーション作成
|
# FastAPI アプリケーション作成
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
@@ -12,13 +14,37 @@ app = FastAPI(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SecurityHeadersMiddleware:
|
||||||
|
"""キャッシュ禁止・セキュリティヘッダーを全レスポンスに付与(ASGIネイティブ実装)”"""
|
||||||
|
def __init__(self, app: ASGIApp) -> None:
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
||||||
|
if scope["type"] != "http":
|
||||||
|
await self.app(scope, receive, send)
|
||||||
|
return
|
||||||
|
|
||||||
|
async def send_with_headers(message):
|
||||||
|
if message["type"] == "http.response.start":
|
||||||
|
headers = MutableHeaders(scope=message)
|
||||||
|
headers["Cache-Control"] = "no-store, no-cache, must-revalidate, private"
|
||||||
|
headers["Pragma"] = "no-cache"
|
||||||
|
headers["Expires"] = "0"
|
||||||
|
headers["X-Frame-Options"] = "DENY"
|
||||||
|
headers["X-Content-Type-Options"] = "nosniff"
|
||||||
|
headers["X-XSS-Protection"] = "1; mode=block"
|
||||||
|
await send(message)
|
||||||
|
|
||||||
|
await self.app(scope, receive, send_with_headers)
|
||||||
|
|
||||||
|
app.add_middleware(SecurityHeadersMiddleware)
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=["*"],
|
allow_origins=["*"],
|
||||||
allow_credentials=False,
|
allow_credentials=False,
|
||||||
allow_methods=["*"],
|
allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
||||||
allow_headers=["*"],
|
allow_headers=["Content-Type", "Authorization"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# ─────────────────────────
|
# ─────────────────────────
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ class LoginRequest(BaseModel):
|
|||||||
password: str
|
password: str
|
||||||
|
|
||||||
|
|
||||||
|
class ChangePasswordRequest(BaseModel):
|
||||||
|
username: str
|
||||||
|
current_password: str
|
||||||
|
new_password: str
|
||||||
|
|
||||||
|
|
||||||
class LoginResponse(BaseModel):
|
class LoginResponse(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
username: str
|
username: str
|
||||||
@@ -66,39 +72,48 @@ async def login(request: LoginRequest):
|
|||||||
|
|
||||||
@router.post("/register")
|
@router.post("/register")
|
||||||
async def register(request: LoginRequest):
|
async def register(request: LoginRequest):
|
||||||
"""新規ユーザー登録"""
|
"""新規ユーザー登録(無効化済み)"""
|
||||||
try:
|
raise HTTPException(status_code=403, detail="新規ユーザー登録は現在受け付けていません")
|
||||||
if len(request.password) < 6:
|
|
||||||
raise HTTPException(status_code=400, detail="パスワードは6文字以上である必要があります")
|
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/change-password")
|
||||||
|
async def change_password(request: ChangePasswordRequest):
|
||||||
|
"""パスワード変更"""
|
||||||
|
if len(request.new_password) < 8:
|
||||||
|
raise HTTPException(status_code=400, detail="新しいパスワードは8文字以上である必要があります")
|
||||||
|
|
||||||
|
try:
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
# ユーザーが既に存在するか確認
|
cur.execute(
|
||||||
cur.execute("SELECT id FROM users WHERE username = %s", (request.username,))
|
"SELECT id, password FROM users WHERE username = %s AND is_active = TRUE",
|
||||||
if cur.fetchone():
|
(request.username,)
|
||||||
|
)
|
||||||
|
user = cur.fetchone()
|
||||||
|
|
||||||
|
if not user:
|
||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
raise HTTPException(status_code=400, detail="このユーザー名は既に使用されています")
|
raise HTTPException(status_code=401, detail="ユーザーが見つかりません")
|
||||||
|
|
||||||
# ユーザーを登録
|
if user["password"] != hash_password(request.current_password):
|
||||||
hashed_password = hash_password(request.password)
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
raise HTTPException(status_code=401, detail="現在のパスワードが正しくありません")
|
||||||
|
|
||||||
|
new_hashed = hash_password(request.new_password)
|
||||||
cur.execute(
|
cur.execute(
|
||||||
"INSERT INTO users (username, password) VALUES (%s, %s) RETURNING id, username",
|
"UPDATE users SET password = %s WHERE username = %s",
|
||||||
(request.username, hashed_password)
|
(new_hashed, request.username)
|
||||||
)
|
)
|
||||||
new_user = cur.fetchone()
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
return {
|
return {"message": "パスワードを変更しました"}
|
||||||
"id": new_user["id"],
|
|
||||||
"username": new_user["username"],
|
|
||||||
"message": "ユーザー登録が完了しました"
|
|
||||||
}
|
|
||||||
|
|
||||||
except HTTPException:
|
except HTTPException:
|
||||||
raise
|
raise
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"登録処理エラー: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"パスワード変更エラー: {str(e)}")
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<title>科目マスタ</title>
|
<title>科目マスタ</title>
|
||||||
<link rel="stylesheet" href="css/style.css" />
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>科目マスタ</h1>
|
<h1>科目マスタ</h1>
|
||||||
|
|||||||
324
frontend/css/mobile.css
Normal file
324
frontend/css/mobile.css
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
/* ==================================================================
|
||||||
|
mobile.css — iOS Safari・Android Chrome レスポンシブ対応
|
||||||
|
ブレークポイント: 767px 以下をモバイルとして扱う
|
||||||
|
PC のレイアウトは変更しない (screen media query で完全に隔離)
|
||||||
|
================================================================== */
|
||||||
|
|
||||||
|
/* ── iOS Safari フォントブースト無効化 ── */
|
||||||
|
html {
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
|
||||||
|
/* ── body 余白を縮小 ── */
|
||||||
|
body {
|
||||||
|
padding: 12px !important;
|
||||||
|
margin: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── h1・h2 フォントサイズ ── */
|
||||||
|
h1 { font-size: 1.35rem !important; }
|
||||||
|
h2 { font-size: 1.1rem !important; }
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
テーブル: 横スクロール
|
||||||
|
会計・給与テーブルは列数が多く、横スクロールが最適解
|
||||||
|
================================================================ */
|
||||||
|
table {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* テーブルを包む overflow:hidden ラッパーも解放する */
|
||||||
|
.doc-table-wrap {
|
||||||
|
overflow-x: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
フォーム入力: 16px で iOS の自動ズームを防ぐ
|
||||||
|
================================================================ */
|
||||||
|
input[type="text"],
|
||||||
|
input[type="date"],
|
||||||
|
input[type="number"],
|
||||||
|
input[type="password"],
|
||||||
|
input[type="email"],
|
||||||
|
input[type="tel"],
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-size: 16px !important;
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
ボタン: タップ領域を 44px 以上に (iOS HIG 推奨)
|
||||||
|
================================================================ */
|
||||||
|
button,
|
||||||
|
input[type="submit"],
|
||||||
|
input[type="button"],
|
||||||
|
.btn,
|
||||||
|
.upload-btn,
|
||||||
|
.back-link,
|
||||||
|
.logout-btn,
|
||||||
|
.change-pw-btn {
|
||||||
|
min-height: 44px;
|
||||||
|
touch-action: manipulation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
index.html
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
/* 2カラムカードグリッド → 1カラム */
|
||||||
|
.grid {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ヘッダー: タイトルとユーザー情報を縦積み */
|
||||||
|
header {
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
font-size: 18px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
width: 100% !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
font-size: 13px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* カード内ボタン群: 縦積み・margin-left をリセット */
|
||||||
|
.card p:last-child {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
display: block !important;
|
||||||
|
text-align: center !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* パスワード変更モーダル: 画面幅に合わせる */
|
||||||
|
.modal-box {
|
||||||
|
width: 92vw !important;
|
||||||
|
max-width: 92vw !important;
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
payroll.html (給与システムメニュー)
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
.menu-grid {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-card {
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
journal-entry.html / journal-edit.html (仕訳入力)
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
/* 検索フォーム: 2カラムグリッド → 1カラム */
|
||||||
|
.search-form-grid {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
gap: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form-row,
|
||||||
|
.search-form-row-full {
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form-row label,
|
||||||
|
.search-form-row-full label {
|
||||||
|
min-width: unset !important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form-row input,
|
||||||
|
.search-form-row select,
|
||||||
|
.search-form-row-full input,
|
||||||
|
.search-form-row-full select {
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 検索ボタン群 */
|
||||||
|
.search-buttons {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-buttons button {
|
||||||
|
flex: 1 1 auto !important;
|
||||||
|
min-width: 100px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .row: 日付・摘要・勘定科目などの入力行 */
|
||||||
|
.row {
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row label {
|
||||||
|
min-width: unset !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row input,
|
||||||
|
.row select {
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
trial-balance.html (試算表)
|
||||||
|
テキスト中心の inline 検索フォームを縦積みにする
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
text-align: left !important;
|
||||||
|
padding: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* trial-balance の検索フォームは label/input が直接子要素 */
|
||||||
|
.search-form > label {
|
||||||
|
display: block !important;
|
||||||
|
margin: 6px 0 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form > input[type="text"],
|
||||||
|
.search-form > input[type="date"],
|
||||||
|
.search-form > input[type="number"] {
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 0 0 8px 15px !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form > button {
|
||||||
|
display: inline-block !important;
|
||||||
|
margin: 4px 4px 0 0 !important;
|
||||||
|
width: auto !important;
|
||||||
|
min-width: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
nenkin-portal.html (社会保険電子文書)
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
flex-direction: column !important;
|
||||||
|
align-items: flex-start !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-zone {
|
||||||
|
padding: 20px 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* フィルターバー: wrapping は既存スタイルで対応済み、幅を補完 */
|
||||||
|
.filter-bar {
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-select {
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
payroll-employees.html / payroll-settings.html (タブ UI)
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
.nav-tabs {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
gap: 6px !important;
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-tab {
|
||||||
|
padding: 8px 14px !important;
|
||||||
|
font-size: 0.85rem !important;
|
||||||
|
border-radius: 6px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* container の余白縮小 */
|
||||||
|
.container {
|
||||||
|
margin: 10px !important;
|
||||||
|
padding: 16px 12px !important;
|
||||||
|
border-radius: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
payroll-settings.html (2/3カラムフォームグリッド → 1カラム)
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row-3 {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input,
|
||||||
|
.form-group select,
|
||||||
|
.form-group textarea {
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
payroll-calculation.html (月次給与計算)
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
/* フィルター行のラベルを縦積み */
|
||||||
|
.filters {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
padding: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters label {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
gap: 4px !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters input,
|
||||||
|
.filters select {
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<title>e-Gov 電子送達 – NJTS</title>
|
<title>e-Gov 電子送達 – NJTS</title>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>NJTS 会計システム - メイン</title>
|
<title>NJTS 会計システム - メイン</title>
|
||||||
<link rel="stylesheet" href="css/style.css" />
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
@@ -79,6 +81,50 @@
|
|||||||
.logout-btn:hover {
|
.logout-btn:hover {
|
||||||
background: #c82333;
|
background: #c82333;
|
||||||
}
|
}
|
||||||
|
.change-pw-btn {
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: #6c757d;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.change-pw-btn:hover { background: #545b62; }
|
||||||
|
/* パスワード変更モーダル */
|
||||||
|
.modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0,0,0,0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.modal-overlay.active { display: flex; }
|
||||||
|
.modal-box {
|
||||||
|
background: white;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 32px;
|
||||||
|
width: 360px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.modal-box h3 { margin: 0 0 20px; }
|
||||||
|
.modal-box .form-group { margin-bottom: 14px; }
|
||||||
|
.modal-box label { display: block; margin-bottom: 5px; font-size: 13px; font-weight: 600; color: #333; }
|
||||||
|
.modal-box input[type="password"] {
|
||||||
|
width: 100%; padding: 10px; border: 1px solid #ddd;
|
||||||
|
border-radius: 5px; font-size: 14px; box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.modal-box .btn-row { display: flex; gap: 10px; margin-top: 20px; }
|
||||||
|
.modal-box .btn-row button { flex: 1; padding: 10px; border: none; border-radius: 5px; cursor: pointer; font-size: 14px; }
|
||||||
|
.modal-box .btn-submit { background: #007bff; color: white; }
|
||||||
|
.modal-box .btn-submit:hover { background: #0056b3; }
|
||||||
|
.modal-box .btn-cancel { background: #e9ecef; color: #333; }
|
||||||
|
.modal-box .btn-cancel:hover { background: #d0d5db; }
|
||||||
|
.modal-msg { padding: 8px 12px; border-radius: 4px; margin-bottom: 12px; display: none; font-size: 13px; }
|
||||||
|
.modal-msg.error { background: #fee; color: #c33; border: 1px solid #fcc; }
|
||||||
|
.modal-msg.success { background: #efe; color: #3a3; border: 1px solid #cfc; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -86,10 +132,35 @@
|
|||||||
<h1>NJTS</h1>
|
<h1>NJTS</h1>
|
||||||
<div class="user-info">
|
<div class="user-info">
|
||||||
<span>ユーザー: <strong id="username">読み込み中...</strong></span>
|
<span>ユーザー: <strong id="username">読み込み中...</strong></span>
|
||||||
|
<button class="change-pw-btn" onclick="openChangePw()">パスワード変更</button>
|
||||||
<button class="logout-btn" onclick="logout()">ログアウト</button>
|
<button class="logout-btn" onclick="logout()">ログアウト</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<!-- パスワード変更モーダル -->
|
||||||
|
<div class="modal-overlay" id="changePwModal">
|
||||||
|
<div class="modal-box">
|
||||||
|
<h3>🔒 パスワード変更</h3>
|
||||||
|
<div class="modal-msg" id="changePwMsg"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>現在のパスワード</label>
|
||||||
|
<input type="password" id="cpCurrent" placeholder="現在のパスワード" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>新しいパスワード(8文字以上)</label>
|
||||||
|
<input type="password" id="cpNew" placeholder="新しいパスワード" />
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>新しいパスワード(確認)</label>
|
||||||
|
<input type="password" id="cpConfirm" placeholder="もう一度入力" />
|
||||||
|
</div>
|
||||||
|
<div class="btn-row">
|
||||||
|
<button class="btn-submit" onclick="submitChangePw()">変更する</button>
|
||||||
|
<button class="btn-cancel" onclick="closeChangePw()">キャンセル</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<main class="grid">
|
<main class="grid">
|
||||||
<section class="card">
|
<section class="card">
|
||||||
<h2>会計システム</h2>
|
<h2>会計システム</h2>
|
||||||
@@ -142,6 +213,56 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// パスワード変更モーダル
|
||||||
|
function openChangePw() {
|
||||||
|
document.getElementById("cpCurrent").value = "";
|
||||||
|
document.getElementById("cpNew").value = "";
|
||||||
|
document.getElementById("cpConfirm").value = "";
|
||||||
|
const msg = document.getElementById("changePwMsg");
|
||||||
|
msg.style.display = "none";
|
||||||
|
document.getElementById("changePwModal").classList.add("active");
|
||||||
|
}
|
||||||
|
function closeChangePw() {
|
||||||
|
document.getElementById("changePwModal").classList.remove("active");
|
||||||
|
}
|
||||||
|
function showChangePwMsg(text, isError) {
|
||||||
|
const msg = document.getElementById("changePwMsg");
|
||||||
|
msg.textContent = text;
|
||||||
|
msg.className = "modal-msg " + (isError ? "error" : "success");
|
||||||
|
msg.style.display = "block";
|
||||||
|
}
|
||||||
|
async function submitChangePw() {
|
||||||
|
const current = document.getElementById("cpCurrent").value;
|
||||||
|
const newPw = document.getElementById("cpNew").value;
|
||||||
|
const confirm = document.getElementById("cpConfirm").value;
|
||||||
|
const user = getCurrentUser();
|
||||||
|
if (!current || !newPw || !confirm) {
|
||||||
|
showChangePwMsg("すべての項目を入力してください", true); return;
|
||||||
|
}
|
||||||
|
if (newPw.length < 8) {
|
||||||
|
showChangePwMsg("新しいパスワードは8文字以上にしてください", true); return;
|
||||||
|
}
|
||||||
|
if (newPw !== confirm) {
|
||||||
|
showChangePwMsg("新しいパスワードが一致しません", true); return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/users/change-password", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ username: user.username, current_password: current, new_password: newPw })
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok) {
|
||||||
|
showChangePwMsg("パスワードを変更しました。再ログインしてください", false);
|
||||||
|
setTimeout(() => { logout(); }, 2000);
|
||||||
|
} else {
|
||||||
|
showChangePwMsg(data.detail || "変更に失敗しました", true);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
showChangePwMsg("エラー: " + e.message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// dropdown toggle
|
// dropdown toggle
|
||||||
const toggle = document.getElementById("accountingToggle");
|
const toggle = document.getElementById("accountingToggle");
|
||||||
const menu = document.getElementById("accountingMenu");
|
const menu = document.getElementById("accountingMenu");
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<title>修正仕訳入力</title>
|
<title>修正仕訳入力</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<title>仕訳入力</title>
|
<title>仕訳入力</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<title>仕訳一覧</title>
|
<title>仕訳一覧</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<title>仕訳詳細</title>
|
<title>仕訳詳細</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<title>仕訳入力</title>
|
<title>仕訳入力</title>
|
||||||
<link rel="stylesheet" href="css/style.css" />
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>仕訳入力</h1>
|
<h1>仕訳入力</h1>
|
||||||
|
|||||||
@@ -1,41 +1,59 @@
|
|||||||
/**
|
/**
|
||||||
* 认证和会话管理
|
* 認証・セッション管理
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 检查用户是否已登录
|
// ──────────────────────────────────────
|
||||||
|
// ページ描画前に即座に認証確認(フラッシュ防止)
|
||||||
|
// ──────────────────────────────────────
|
||||||
|
(function () {
|
||||||
|
if (!window.location.pathname.includes("login.html")) {
|
||||||
|
if (!localStorage.getItem("currentUser")) {
|
||||||
|
// ページが見えてしまわないよう即座に非表示にしてからリダイレクト
|
||||||
|
document.documentElement.style.visibility = "hidden";
|
||||||
|
window.location.replace("/login.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
// bfcache(戻る/進むボタン)対応
|
||||||
|
// ブラウザがキャッシュからページを復元した場合も認証を再確認する
|
||||||
|
window.addEventListener("pageshow", function (event) {
|
||||||
|
if (!window.location.pathname.includes("login.html")) {
|
||||||
|
if (!localStorage.getItem("currentUser")) {
|
||||||
|
// bfcacheから復元されたページを即座に非表示にしてリダイレクト
|
||||||
|
document.documentElement.style.visibility = "hidden";
|
||||||
|
window.location.replace("/login.html");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function checkAuth() {
|
function checkAuth() {
|
||||||
const user = localStorage.getItem("currentUser");
|
const user = localStorage.getItem("currentUser");
|
||||||
if (!user) {
|
if (!user) {
|
||||||
// 用户未登录,重定向到登录页面
|
window.location.replace("/login.html");
|
||||||
window.location.href = "/login.html";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前登录的用户信息
|
|
||||||
function getCurrentUser() {
|
function getCurrentUser() {
|
||||||
const user = localStorage.getItem("currentUser");
|
const user = localStorage.getItem("currentUser");
|
||||||
return user ? JSON.parse(user) : null;
|
return user ? JSON.parse(user) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存用户信息(登录时使用)
|
|
||||||
function setCurrentUser(userData) {
|
function setCurrentUser(userData) {
|
||||||
localStorage.setItem("currentUser", JSON.stringify(userData));
|
localStorage.setItem("currentUser", JSON.stringify(userData));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注销用户
|
|
||||||
function logout() {
|
function logout() {
|
||||||
localStorage.removeItem("currentUser");
|
localStorage.removeItem("currentUser");
|
||||||
window.location.href = "/login.html";
|
sessionStorage.clear();
|
||||||
|
// replace() を使うことでログイン前の履歴に戻れなくなる
|
||||||
|
window.location.replace("/login.html");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在页面加载时检查认证(仅在非登录页面使用)
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
const currentPage = window.location.pathname;
|
if (!window.location.pathname.includes("login.html")) {
|
||||||
|
|
||||||
// 排除登录页面的检查
|
|
||||||
if (!currentPage.includes("login.html")) {
|
|
||||||
checkAuth();
|
checkAuth();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<title>元帳</title>
|
<title>元帳</title>
|
||||||
<link rel="stylesheet" href="css/style.css" />
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1 id="account-title">元帳</h1>
|
<h1 id="account-title">元帳</h1>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>NJTS 会計システム - ログイン</title>
|
<title>NJTS 会計システム - ログイン</title>
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -108,16 +109,6 @@
|
|||||||
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-register {
|
|
||||||
background: transparent;
|
|
||||||
color: #667eea;
|
|
||||||
border: 2px solid #667eea;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-register:hover {
|
|
||||||
background: #f0f0ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@@ -138,32 +129,6 @@
|
|||||||
border: 1px solid #cfc;
|
border: 1px solid #cfc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-form {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-form a {
|
|
||||||
color: #667eea;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toggle-form a:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form.active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading {
|
.loading {
|
||||||
display: none;
|
display: none;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@@ -181,7 +146,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- ログインフォーム -->
|
<!-- ログインフォーム -->
|
||||||
<div id="loginForm" class="form active">
|
<div id="loginForm">
|
||||||
<div id="messageLogin" class="message"></div>
|
<div id="messageLogin" class="message"></div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -208,47 +173,8 @@
|
|||||||
<button class="btn-login" onclick="handleLogin()">ログイン</button>
|
<button class="btn-login" onclick="handleLogin()">ログイン</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="toggle-form">
|
|
||||||
新規登録?
|
|
||||||
<a onclick="toggleForm()">アカウント作成</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" id="loginLoading">処理中...</div>
|
<div class="loading" id="loginLoading">処理中...</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 登録フォーム -->
|
|
||||||
<div id="registerForm" class="form">
|
|
||||||
<div id="messageRegister" class="message"></div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="regUsername">ユーザー名</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="regUsername"
|
|
||||||
placeholder="ユーザー名を入力"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="regPassword">パスワード</label>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="regPassword"
|
|
||||||
placeholder="6文字以上で設定"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="button-group">
|
|
||||||
<button class="btn-login" onclick="handleRegister()">登録</button>
|
|
||||||
<button class="btn-register" onclick="toggleForm()">
|
|
||||||
キャンセル
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="loading" id="registerLoading">処理中...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -330,82 +256,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleRegister() {
|
// EnterキーでログインできるようにEnterキーをバインド
|
||||||
const username = document.getElementById("regUsername").value;
|
|
||||||
const password = document.getElementById("regPassword").value;
|
|
||||||
|
|
||||||
if (!username || !password) {
|
|
||||||
showMessage(
|
|
||||||
"messageRegister",
|
|
||||||
"ユーザー名とパスワードを入力してください",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 6) {
|
|
||||||
showMessage(
|
|
||||||
"messageRegister",
|
|
||||||
"パスワードは6文字以上である必要があります",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadingEl = document.getElementById("registerLoading");
|
|
||||||
loadingEl.style.display = "block";
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/users/register", {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ username, password }),
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
showMessage(
|
|
||||||
"messageRegister",
|
|
||||||
"登録が完了しました。ログインしてください",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
setTimeout(() => {
|
|
||||||
toggleForm();
|
|
||||||
document.getElementById("loginUsername").value = username;
|
|
||||||
document.getElementById("loginPassword").value = "";
|
|
||||||
}, 2000);
|
|
||||||
} else {
|
|
||||||
showMessage(
|
|
||||||
"messageRegister",
|
|
||||||
data.detail || "登録に失敗しました",
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
showMessage(
|
|
||||||
"messageRegister",
|
|
||||||
"エラーが発生しました: " + error.message,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
loadingEl.style.display = "none";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enterキーでログイン/登録
|
|
||||||
document.addEventListener("keypress", (e) => {
|
document.addEventListener("keypress", (e) => {
|
||||||
if (e.key === "Enter") {
|
if (e.key === "Enter") {
|
||||||
const loginForm = document.getElementById("loginForm");
|
|
||||||
if (loginForm.classList.contains("active")) {
|
|
||||||
handleLogin();
|
handleLogin();
|
||||||
} else {
|
|
||||||
handleRegister();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ログインページから後退ボタンで離脱できないようにする
|
||||||
|
// (ログアウト後に戻るボタンでメニューが見えてしまう問題の対策)
|
||||||
|
history.pushState(null, "", window.location.href);
|
||||||
|
window.addEventListener("popstate", function () {
|
||||||
|
history.pushState(null, "", window.location.href);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<title>社会保険電子文書ポータル</title>
|
<title>社会保険電子文書ポータル</title>
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<link rel="stylesheet" href="css/style.css" />
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: system-ui, -apple-system, "Yu Gothic UI", "Hiragino Kaku Gothic ProN", "メイリオ", sans-serif;
|
font-family: system-ui, -apple-system, "Yu Gothic UI", "Hiragino Kaku Gothic ProN", "メイリオ", sans-serif;
|
||||||
|
|||||||
@@ -2,9 +2,11 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<title>期首残高入力</title>
|
<title>期首残高入力</title>
|
||||||
<link rel="stylesheet" href="css/style.css" />
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>期首残高入力</h1>
|
<h1>期首残高入力</h1>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>給与管理 - 月次給与計算</title>
|
<title>給与管理 - 月次給与計算</title>
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
.container {
|
.container {
|
||||||
max-width: 1440px;
|
max-width: 1440px;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
/>
|
/>
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: "Noto Sans JP", Arial, sans-serif;
|
font-family: "Noto Sans JP", Arial, sans-serif;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>給与管理 - 給与設定</title>
|
<title>給与管理 - 給与設定</title>
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
.employee-select {
|
.employee-select {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>給与管理 - 設定</title>
|
<title>給与管理 - 設定</title>
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
.nav-tabs {
|
.nav-tabs {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>給与管理システム</title>
|
<title>給与管理システム</title>
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
.menu-grid {
|
.menu-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
<html lang="ja">
|
<html lang="ja">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<script src="js/auth.js"></script>
|
<script src="js/auth.js"></script>
|
||||||
<title>試算表</title>
|
<title>試算表</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
font-family: "MS Gothic", "Meiryo", sans-serif;
|
font-family: "MS Gothic", "Meiryo", sans-serif;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>給与所得の源泉徴収票</title>
|
<title>給与所得の源泉徴収票</title>
|
||||||
<link rel="stylesheet" href="/css/style.css" />
|
<link rel="stylesheet" href="/css/style.css" />
|
||||||
|
<link rel="stylesheet" href="/css/mobile.css" />
|
||||||
<style>
|
<style>
|
||||||
/* ===== 画面UI ===== */
|
/* ===== 画面UI ===== */
|
||||||
body { font-family: 'MS Gothic', 'Meiryo', sans-serif; }
|
body { font-family: 'MS Gothic', 'Meiryo', sans-serif; }
|
||||||
|
|||||||
Reference in New Issue
Block a user