Compare commits
merge into: qimaohong:master
qimaohong:dev
qimaohong:heads/milestone-20251128-获客渠道
qimaohong:master
qimaohong:milestone-20251013-宣传夺宝奇兵
qimaohong:milestone-20251107-后台登录页面
pull from: qimaohong:heads/milestone-20251128-获客渠道
qimaohong:dev
qimaohong:heads/milestone-20251128-获客渠道
qimaohong:master
qimaohong:milestone-20251013-宣传夺宝奇兵
qimaohong:milestone-20251107-后台登录页面
15 Commits
master
...
heads/mile
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
0bd8fb4e07 |
.gitignore
|
1 month ago |
|
|
e49962aeb5 |
feat:获取视频地址
|
1 month ago |
|
|
2e67fdb7c1 |
feat:获取获客来源
|
1 month ago |
|
|
8b2522ceee |
修改打包文件
|
2 months ago |
|
|
d08f006680 |
登录页面
|
2 months ago |
|
|
1067f220c3 |
Merge branch 'milestone-20251013-宣传夺宝奇兵' into dev
|
2 months ago |
|
|
e21a563222 |
后台添加whatsapp字段实现动态跳转
|
2 months ago |
|
|
02d0308cde |
Merge branch 'milestone-20251013-宣传夺宝奇兵' into dev
|
2 months ago |
|
|
35688a94e3 |
后台添加whatsapp字段实现动态跳转
|
2 months ago |
|
|
2968989607 |
网太卡直接提交了样式
|
3 months ago |
|
|
b3abd03f66 |
添加定位
|
3 months ago |
|
|
419b7e964e |
修改定位
|
3 months ago |
|
|
e910b5b01c |
修改图片
|
3 months ago |
|
|
708f6c2008 |
修改字体
|
3 months ago |
|
|
c6eac6807e |
修改背景图片即文字
|
3 months ago |
15 changed files with 1978 additions and 712 deletions
-
2.gitignore
-
2dist-test/hcdbqb-download.html
-
2dist-test/hcdbqb-guide.html
-
488dist-test/hcdbqb-management.html
-
135dist-test/index.html
-
351dist-test/login-management.html
-
2dist-test/main.js
-
11dist-test/management.js
-
1031hcdbqb-management.html
-
155index.html
-
351login-management.html
-
10node_modules/.vite/deps/_metadata.json
-
11node_modules/.vue-global-types/vue_3.5_0.d.ts
-
138node_modules/.vue-global-types/vue_99_0.d.ts
-
1vite.config.js
@ -0,0 +1,2 @@ |
|||
/node_modules |
|||
/dist-test |
|||
@ -1,222 +1,298 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="zh-CN"> |
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
<meta name="viewport" content="width=device-width,initial-scale=1" /> |
|||
<title>管理后台</title> |
|||
<style> |
|||
body { |
|||
font-family: Arial, sans-serif; |
|||
padding: 24px; |
|||
background: #f7f8fb; |
|||
color: #222; |
|||
} |
|||
.card { |
|||
background: #fff; |
|||
padding: 16px; |
|||
border-radius: 8px; |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); |
|||
margin: auto; |
|||
} |
|||
table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
margin-top: 12px; |
|||
} |
|||
th, |
|||
td { |
|||
padding: 10px 12px; |
|||
border-bottom: 1px solid #eee; |
|||
text-align: left; |
|||
font-size: 14px; |
|||
} |
|||
th { |
|||
background: #fafafa; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
<head> |
|||
<meta charset="utf-8" /> |
|||
<meta name="viewport" content="width=device-width,initial-scale=1" /> |
|||
<title>管理后台</title> |
|||
<style> |
|||
body { |
|||
font-family: Arial, sans-serif; |
|||
padding: 24px; |
|||
background: #f7f8fb; |
|||
color: #222; |
|||
} |
|||
|
|||
.card { |
|||
background: #fff; |
|||
padding: 16px; |
|||
border-radius: 8px; |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); |
|||
margin: auto; |
|||
} |
|||
|
|||
table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
margin-top: 12px; |
|||
} |
|||
|
|||
th, |
|||
td { |
|||
padding: 10px 12px; |
|||
border-bottom: 1px solid #eee; |
|||
text-align: left; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
th { |
|||
background: #fafafa; |
|||
font-weight: 600; |
|||
} |
|||
|
|||
.controls { |
|||
display: flex; |
|||
gap: 12px; |
|||
align-items: center; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.pagination { |
|||
display: flex; |
|||
gap: 6px; |
|||
align-items: center; |
|||
margin-left: auto; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.btn { |
|||
padding: 6px 10px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ddd; |
|||
background: #fff; |
|||
cursor: pointer; |
|||
user-select: none; |
|||
} |
|||
|
|||
.btn:disabled { |
|||
opacity: 0.5; |
|||
cursor: default; |
|||
} |
|||
|
|||
.btn.primary { |
|||
background: #007bff; |
|||
color: #fff; |
|||
border-color: #007bff; |
|||
} |
|||
|
|||
.btn.whatsapp { |
|||
background: #25D366; |
|||
color: #fff; |
|||
border-color: #25D366; |
|||
} |
|||
|
|||
.status-btn { |
|||
padding: 4px 8px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ccc; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.status-0 { |
|||
background: red; |
|||
color: #fff; |
|||
} |
|||
|
|||
.status-1 { |
|||
background: #2f9e44; |
|||
color: #fff; |
|||
border-color: #2f9e44; |
|||
} |
|||
|
|||
.btn.active { |
|||
background: #007bff; |
|||
color: #fff; |
|||
border-color: #007bff; |
|||
} |
|||
|
|||
select, |
|||
input[type="number"] { |
|||
padding: 6px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ddd; |
|||
} |
|||
|
|||
.small { |
|||
font-size: 13px; |
|||
color: #666; |
|||
} |
|||
|
|||
@media (max-width: 640px) { |
|||
.controls { |
|||
display: flex; |
|||
gap: 12px; |
|||
align-items: center; |
|||
flex-wrap: wrap; |
|||
flex-direction: column; |
|||
align-items: flex-start; |
|||
} |
|||
|
|||
.pagination { |
|||
display: flex; |
|||
gap: 6px; |
|||
align-items: center; |
|||
margin-left: auto; |
|||
flex-wrap: wrap; |
|||
} |
|||
.btn { |
|||
padding: 6px 10px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ddd; |
|||
background: #fff; |
|||
cursor: pointer; |
|||
user-select: none; |
|||
} |
|||
.btn:disabled { |
|||
opacity: 0.5; |
|||
cursor: default; |
|||
} |
|||
.btn.primary { |
|||
background: #007bff; |
|||
color: #fff; |
|||
border-color: #007bff; |
|||
} |
|||
.status-btn { |
|||
padding: 4px 8px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ccc; |
|||
cursor: pointer; |
|||
} |
|||
.status-0 { |
|||
background: red; |
|||
color: #fff; |
|||
} |
|||
.status-1 { |
|||
background: #2f9e44; |
|||
color: #fff; |
|||
border-color: #2f9e44; |
|||
} |
|||
.btn.active { |
|||
background: #007bff; |
|||
color: #fff; |
|||
border-color: #007bff; |
|||
} |
|||
select, |
|||
input[type="number"] { |
|||
padding: 6px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ddd; |
|||
margin-left: 0; |
|||
} |
|||
.small { |
|||
font-size: 13px; |
|||
color: #666; |
|||
} |
|||
@media (max-width: 640px) { |
|||
.controls { |
|||
flex-direction: column; |
|||
align-items: flex-start; |
|||
} |
|||
.pagination { |
|||
margin-left: 0; |
|||
} |
|||
} |
|||
#noteModal { |
|||
display: none; |
|||
position: fixed; |
|||
inset: 0; |
|||
background: rgba(0, 0, 0, 0.45); |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 9999; |
|||
} |
|||
#noteModal .dialog { |
|||
background: #fff; |
|||
padding: 16px; |
|||
border-radius: 8px; |
|||
width: 90%; |
|||
max-width: 520px; |
|||
box-sizing: border-box; |
|||
} |
|||
#noteModal textarea { |
|||
width: 100%; |
|||
min-width: 60%; |
|||
max-width: 100%; |
|||
min-height: 150px; |
|||
box-sizing: border-box; |
|||
padding: 8px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ddd; |
|||
font-size: 14px; |
|||
} |
|||
.toast { |
|||
position: fixed; |
|||
top: -20px; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
background: #4caf50; |
|||
color: #fff; |
|||
padding: 10px 16px; |
|||
border-radius: 6px; |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); |
|||
font-size: 16px; |
|||
opacity: 0; |
|||
transition: all 0.3s ease; |
|||
z-index: 10000; |
|||
pointer-events: none; |
|||
} |
|||
.toast.show { |
|||
opacity: 1; |
|||
top: 20px; |
|||
} |
|||
table th, |
|||
table td { |
|||
text-align: center; |
|||
} |
|||
</style> |
|||
<script type="module" crossorigin src="./management.js"></script> |
|||
<link rel="modulepreload" crossorigin href="./member.js"> |
|||
</head> |
|||
<body> |
|||
<div class="card"> |
|||
<table aria-describedby="tableDesc"> |
|||
<thead> |
|||
<tr> |
|||
<th style="width: 40px">#</th> |
|||
<th style="width: 100px">姓名</th> |
|||
<th style="width: 90px">国家/地区代码</th> |
|||
<th style="width: 120px">电话号码</th> |
|||
<th style="width: 120px">微信ID</th> |
|||
<th style="width: 150px">邮箱</th> |
|||
<th style="width: 160px">添加时间</th> |
|||
<th style="width: 100px">是否联系</th> |
|||
<th style="width: 160px">备注</th> |
|||
<th style="width: 100px">操作</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody id="tableBody"></tbody> |
|||
</table> |
|||
<div class="controls" style="margin-bottom: 8px; margin-top: 12px"> |
|||
<div class="small"> |
|||
每页显示 |
|||
<select id="pageSizeSelect"> |
|||
<option value="20" selected>20</option> |
|||
<option value="50">50</option> |
|||
<option value="100">100</option> |
|||
<option value="200">200</option> |
|||
</select> |
|||
条 |
|||
</div> |
|||
} |
|||
|
|||
#noteModal { |
|||
display: none; |
|||
position: fixed; |
|||
inset: 0; |
|||
background: rgba(0, 0, 0, 0.45); |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 9999; |
|||
} |
|||
|
|||
#noteModal .dialog { |
|||
background: #fff; |
|||
padding: 16px; |
|||
border-radius: 8px; |
|||
width: 90%; |
|||
max-width: 520px; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
#noteModal textarea { |
|||
width: 100%; |
|||
min-width: 60%; |
|||
max-width: 100%; |
|||
min-height: 150px; |
|||
box-sizing: border-box; |
|||
padding: 8px; |
|||
border-radius: 6px; |
|||
border: 1px solid #ddd; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.toast { |
|||
position: fixed; |
|||
top: -20px; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
background: #4caf50; |
|||
color: #fff; |
|||
padding: 10px 16px; |
|||
border-radius: 6px; |
|||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); |
|||
font-size: 16px; |
|||
opacity: 0; |
|||
transition: all 0.3s ease; |
|||
z-index: 10000; |
|||
pointer-events: none; |
|||
} |
|||
|
|||
.toast.show { |
|||
opacity: 1; |
|||
top: 20px; |
|||
} |
|||
|
|||
table th, |
|||
table td { |
|||
text-align: center; |
|||
} |
|||
|
|||
/* 登录验证样式 */ |
|||
#loginCheckModal { |
|||
display: none; |
|||
position: fixed; |
|||
inset: 0; |
|||
background: rgba(0, 0, 0, 0.7); |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 99999; |
|||
} |
|||
|
|||
#loginCheckModal .dialog { |
|||
background: #fff; |
|||
padding: 30px; |
|||
border-radius: 12px; |
|||
width: 90%; |
|||
max-width: 400px; |
|||
text-align: center; |
|||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); |
|||
} |
|||
|
|||
#loginCheckModal h3 { |
|||
margin: 0 0 16px; |
|||
color: #e74c3c; |
|||
font-size: 20px; |
|||
} |
|||
|
|||
#loginCheckModal p { |
|||
margin: 0 0 24px; |
|||
color: #666; |
|||
line-height: 1.5; |
|||
} |
|||
|
|||
#loginCheckModal .btn { |
|||
padding: 10px 20px; |
|||
font-size: 16px; |
|||
} |
|||
</style> |
|||
<script type="module" crossorigin src="./management.js"></script> |
|||
<link rel="modulepreload" crossorigin href="./member.js"> |
|||
</head> |
|||
|
|||
<div class="small">共 <span id="totalCount">0</span> 条</div> |
|||
<body> |
|||
<!-- 登录验证模态框 --> |
|||
<div id="loginCheckModal"> |
|||
<div class="dialog"> |
|||
<h3>访问被拒绝</h3> |
|||
<p>您尚未登录或登录已过期,请先登录系统。</p> |
|||
<button class="btn primary" id="goToLoginBtn">前往登录页面</button> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="pagination" id="pagination"></div> |
|||
<div class="card"> |
|||
<table aria-describedby="tableDesc"> |
|||
<thead> |
|||
<tr> |
|||
<th style="width: 30px">#</th> |
|||
<th style="width: 60px">姓名</th> |
|||
<th style="width: 80px">WhatsApp</th> |
|||
<th style="width: 90px">国家/地区代码</th> |
|||
<th style="width: 110px">电话号码</th> |
|||
<th style="width: 110px">微信ID</th> |
|||
<th style="width: 110px">邮箱</th> |
|||
<th style="width: 70px">获客来源</th> |
|||
<th style="width: 130px">来源地址</th> |
|||
<th style="width: 150px">添加时间</th> |
|||
<th style="width: 90px">是否联系</th> |
|||
<th style="width: 90px">备注</th> |
|||
<th style="width: 100px">操作</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody id="tableBody"></tbody> |
|||
</table> |
|||
<div class="controls" style="margin-bottom: 8px; margin-top: 12px"> |
|||
<div class="small"> |
|||
每页显示 |
|||
<select id="pageSizeSelect"> |
|||
<option value="20" selected>20</option> |
|||
<option value="50">50</option> |
|||
<option value="100">100</option> |
|||
<option value="200">200</option> |
|||
</select> |
|||
条 |
|||
</div> |
|||
<!-- 备注编辑模态 --> |
|||
<div id="noteModal"> |
|||
<div class="dialog"> |
|||
<h3 style="margin: 0 0 8px">编辑备注</h3> |
|||
<textarea |
|||
id="noteTextarea" |
|||
rows="6" |
|||
placeholder="输入备注..." |
|||
></textarea> |
|||
<div |
|||
style=" |
|||
|
|||
<div class="small">共 <span id="totalCount">0</span> 条</div> |
|||
|
|||
<div class="pagination" id="pagination"></div> |
|||
</div> |
|||
<!-- 备注编辑模态 --> |
|||
<div id="noteModal"> |
|||
<div class="dialog"> |
|||
<h3 style="margin: 0 0 8px">编辑备注</h3> |
|||
<textarea id="noteTextarea" rows="6" placeholder="输入备注..."></textarea> |
|||
<div style=" |
|||
margin-top: 10px; |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
gap: 8px; |
|||
" |
|||
> |
|||
<button class="btn" id="noteCancelBtn">取消</button> |
|||
<button class="btn primary" id="noteSaveBtn">保存</button> |
|||
</div> |
|||
"> |
|||
<button class="btn" id="noteCancelBtn">取消</button> |
|||
<button class="btn primary" id="noteSaveBtn">保存</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div id="toast" class="toast"></div> |
|||
</body> |
|||
</html> |
|||
</div> |
|||
<div id="toast" class="toast"></div> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,351 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="zh-CN"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>系统登录</title> |
|||
<style> |
|||
* { |
|||
margin: 0; |
|||
padding: 0; |
|||
box-sizing: border-box; |
|||
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif; |
|||
} |
|||
|
|||
body { |
|||
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
min-height: 100vh; |
|||
padding: 20px; |
|||
} |
|||
|
|||
.login-container { |
|||
background-color: white; |
|||
border-radius: 12px; |
|||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); |
|||
width: 100%; |
|||
max-width: 420px; |
|||
padding: 40px 30px; |
|||
transition: transform 0.3s ease; |
|||
} |
|||
|
|||
.login-container:hover { |
|||
transform: translateY(-5px); |
|||
} |
|||
|
|||
.login-header { |
|||
text-align: center; |
|||
margin-bottom: 30px; |
|||
} |
|||
|
|||
.login-header h1 { |
|||
color: #333; |
|||
font-size: 28px; |
|||
margin-bottom: 8px; |
|||
} |
|||
|
|||
.login-header p { |
|||
color: #666; |
|||
font-size: 16px; |
|||
} |
|||
|
|||
.form-group { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
label { |
|||
display: block; |
|||
margin-bottom: 8px; |
|||
color: #555; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
input { |
|||
width: 100%; |
|||
padding: 14px 16px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 8px; |
|||
font-size: 16px; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
input:focus { |
|||
border-color: #4a90e2; |
|||
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2); |
|||
outline: none; |
|||
} |
|||
|
|||
.error-message { |
|||
color: #e74c3c; |
|||
font-size: 14px; |
|||
margin-top: 5px; |
|||
display: none; |
|||
} |
|||
|
|||
.login-button { |
|||
background: linear-gradient(to right, #6a11cb, #2575fc); |
|||
color: white; |
|||
border: none; |
|||
border-radius: 8px; |
|||
padding: 14px; |
|||
font-size: 16px; |
|||
font-weight: 600; |
|||
cursor: pointer; |
|||
width: 100%; |
|||
transition: all 0.3s; |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.login-button:hover { |
|||
background: linear-gradient(to right, #5a0db5, #1c68e8); |
|||
box-shadow: 0 5px 15px rgba(37, 117, 252, 0.4); |
|||
} |
|||
|
|||
.login-button:active { |
|||
transform: scale(0.98); |
|||
} |
|||
|
|||
.login-footer { |
|||
text-align: center; |
|||
margin-top: 25px; |
|||
color: #777; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.alert { |
|||
padding: 12px 16px; |
|||
border-radius: 8px; |
|||
margin-bottom: 20px; |
|||
display: none; |
|||
} |
|||
|
|||
.alert-error { |
|||
background-color: #ffebee; |
|||
color: #c62828; |
|||
border: 1px solid #ffcdd2; |
|||
} |
|||
|
|||
.alert-success { |
|||
background-color: #e8f5e9; |
|||
color: #2e7d32; |
|||
border: 1px solid #c8e6c9; |
|||
} |
|||
|
|||
.password-requirements { |
|||
font-size: 12px; |
|||
color: #777; |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
.input-error { |
|||
border-color: #e74c3c; |
|||
box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.2); |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<div class="login-container"> |
|||
<div class="login-header"> |
|||
<h1>系统登录</h1> |
|||
<p>请输入您的账号和密码</p> |
|||
</div> |
|||
|
|||
<div id="usernameAlert" class="alert alert-error"> |
|||
账号不存在,请检查后重试! |
|||
</div> |
|||
|
|||
<div id="passwordAlert" class="alert alert-error"> |
|||
密码错误,请检查后重试! |
|||
</div> |
|||
|
|||
<form id="loginForm"> |
|||
<div class="form-group"> |
|||
<label for="username">账号</label> |
|||
<input type="text" id="username" name="username" placeholder="请输入账号" required> |
|||
<div class="error-message" id="usernameError">账号不能为空</div> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="password">密码</label> |
|||
<input type="password" id="password" name="password" placeholder="请输入密码" required> |
|||
<div class="error-message" id="passwordError">密码不能为空</div> |
|||
<div class="password-requirements">密码必须为8位数字</div> |
|||
</div> |
|||
|
|||
<button type="submit" class="login-button">登录</button> |
|||
</form> |
|||
</div> |
|||
|
|||
<script> |
|||
document.addEventListener('DOMContentLoaded', function () { |
|||
const loginForm = document.getElementById('loginForm'); |
|||
const usernameInput = document.getElementById('username'); |
|||
const passwordInput = document.getElementById('password'); |
|||
const usernameError = document.getElementById('usernameError'); |
|||
const passwordError = document.getElementById('passwordError'); |
|||
const usernameAlert = document.getElementById('usernameAlert'); |
|||
const passwordAlert = document.getElementById('passwordAlert'); |
|||
|
|||
// 正确的账号和密码 |
|||
const CORRECT_USERNAME = 'admin'; |
|||
const CORRECT_PASSWORD = '20251107'; |
|||
|
|||
// 表单提交事件 |
|||
loginForm.addEventListener('submit', function (e) { |
|||
e.preventDefault(); |
|||
|
|||
// 重置错误状态 |
|||
resetErrors(); |
|||
|
|||
// 获取表单数据 |
|||
const username = usernameInput.value.trim(); |
|||
const password = passwordInput.value.trim(); |
|||
|
|||
// 表单验证 |
|||
let isValid = true; |
|||
|
|||
if (!username) { |
|||
showError(usernameError, '账号不能为空'); |
|||
usernameInput.classList.add('input-error'); |
|||
isValid = false; |
|||
} |
|||
|
|||
if (!password) { |
|||
showError(passwordError, '密码不能为空'); |
|||
passwordInput.classList.add('input-error'); |
|||
isValid = false; |
|||
} else if (!/^\d{8}$/.test(password)) { |
|||
showError(passwordError, '密码必须为8位数字'); |
|||
passwordInput.classList.add('input-error'); |
|||
isValid = false; |
|||
} |
|||
|
|||
// 如果验证通过,尝试登录 |
|||
if (isValid) { |
|||
// 验证账号密码 |
|||
if (username === CORRECT_USERNAME && password === CORRECT_PASSWORD) { |
|||
// 登录成功,保存登录状态 |
|||
localStorage.setItem('isLoggedIn', 'true'); |
|||
localStorage.setItem('loginTime', new Date().getTime()); |
|||
|
|||
// 显示成功消息(短暂显示后跳转) |
|||
showSuccessMessage(); |
|||
|
|||
// 延迟跳转,让用户看到成功消息 |
|||
setTimeout(function () { |
|||
window.location.href = 'hcdbqb-management.html'; |
|||
}, 1000); |
|||
} else { |
|||
// 登录失败,明确提示是账号还是密码错误 |
|||
if (username !== CORRECT_USERNAME) { |
|||
usernameAlert.style.display = 'block'; |
|||
usernameInput.classList.add('input-error'); |
|||
usernameInput.focus(); |
|||
} else { |
|||
passwordAlert.style.display = 'block'; |
|||
passwordInput.classList.add('input-error'); |
|||
passwordInput.value = ''; |
|||
passwordInput.focus(); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
// 实时验证 |
|||
usernameInput.addEventListener('blur', function () { |
|||
if (!this.value.trim()) { |
|||
showError(usernameError, '账号不能为空'); |
|||
this.classList.add('input-error'); |
|||
} else { |
|||
hideError(usernameError); |
|||
this.classList.remove('input-error'); |
|||
} |
|||
}); |
|||
|
|||
passwordInput.addEventListener('blur', function () { |
|||
const password = this.value.trim(); |
|||
if (!password) { |
|||
showError(passwordError, '密码不能为空'); |
|||
this.classList.add('input-error'); |
|||
} else if (!/^\d{8}$/.test(password)) { |
|||
showError(passwordError, '密码必须为8位数字'); |
|||
this.classList.add('input-error'); |
|||
} else { |
|||
hideError(passwordError); |
|||
this.classList.remove('input-error'); |
|||
} |
|||
}); |
|||
|
|||
// 输入时隐藏错误提示 |
|||
usernameInput.addEventListener('input', function () { |
|||
hideError(usernameError); |
|||
hideAlert(usernameAlert); |
|||
this.classList.remove('input-error'); |
|||
}); |
|||
|
|||
passwordInput.addEventListener('input', function () { |
|||
hideError(passwordError); |
|||
hideAlert(passwordAlert); |
|||
this.classList.remove('input-error'); |
|||
}); |
|||
|
|||
// 辅助函数 |
|||
function showError(element, message) { |
|||
element.textContent = message; |
|||
element.style.display = 'block'; |
|||
} |
|||
|
|||
function hideError(element) { |
|||
element.style.display = 'none'; |
|||
} |
|||
|
|||
function hideAlert(alertElement) { |
|||
alertElement.style.display = 'none'; |
|||
} |
|||
|
|||
function resetErrors() { |
|||
hideError(usernameError); |
|||
hideError(passwordError); |
|||
hideAlert(usernameAlert); |
|||
hideAlert(passwordAlert); |
|||
usernameInput.classList.remove('input-error'); |
|||
passwordInput.classList.remove('input-error'); |
|||
} |
|||
|
|||
function showSuccessMessage() { |
|||
// 临时创建一个成功提示 |
|||
const successAlert = document.createElement('div'); |
|||
successAlert.className = 'alert alert-success'; |
|||
successAlert.textContent = '登录成功,正在跳转...'; |
|||
usernameAlert.parentNode.insertBefore(successAlert, usernameAlert); |
|||
successAlert.style.display = 'block'; |
|||
|
|||
// 移除错误提示(如果有) |
|||
hideAlert(usernameAlert); |
|||
hideAlert(passwordAlert); |
|||
} |
|||
|
|||
// 检查是否已经登录(如果已经登录,直接跳转) |
|||
if (localStorage.getItem('isLoggedIn') === 'true') { |
|||
// 检查登录时间,如果超过24小时需要重新登录 |
|||
const loginTime = parseInt(localStorage.getItem('loginTime')); |
|||
const currentTime = new Date().getTime(); |
|||
const hoursDiff = (currentTime - loginTime) / (1000 * 60 * 60); |
|||
|
|||
if (hoursDiff < 24) { |
|||
window.location.href = 'hcdbqb-management.html'; |
|||
} else { |
|||
// 超过24小时,清除登录状态 |
|||
localStorage.removeItem('isLoggedIn'); |
|||
localStorage.removeItem('loginTime'); |
|||
} |
|||
} |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -1 +1 @@ |
|||
import{g as E,r as h}from"./member.js";const I=50,L=100,B=20,u=document.getElementById("registrationForm"),l=document.getElementById("successModal"),p=document.getElementById("closeModal"),a=document.getElementById("submitBtn"),g=document.getElementById("loadingOverlay"),i=document.getElementById("lectureImage");let r=!1;async function v(){console.log("开始加载讲座图片...");try{const e={id:1};console.log("调用getImageApi,参数:",e);const t=await E(e);console.log("接口返回:",t),t.code===200&&t.data?(i.src=t.data,console.log("讲座图片加载成功:",t.data)):(console.warn("获取图片失败:",t.msg),i.src="https://hc.homilychart.com/hc/250121/img/kecheng.jpg")}catch(e){console.error("加载讲座图片失败:",e),i.src="https://hc.homilychart.com/hc/250121/img/kecheng.jpg"}}function w(){g.style.display="flex",a.disabled=!0,a.textContent="提交中...",r=!0}function M(){g.style.display="none",a.disabled=!1,a.textContent="提交注册",r=!1}function s(e){alert(e)}function b(e){const{name:t,tel:o,email:n}=e;return!t||t.length>I?(s("请输入有效的姓名(最多50个字符)"),document.getElementById("userNameInfo").focus(),!1):!o||o.length>B?(s("请输入有效的电话号码"),document.getElementById("userMobile").focus(),!1):!n||n.length>L?(s("请输入电子邮箱"),document.getElementById("userEmail").focus(),!1):/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(n)?!0:(s("请输入有效的电子邮箱地址"),document.getElementById("userEmail").focus(),!1)}async function A(e){if(e.preventDefault(),r)return;const t=document.getElementById("userNameInfo").value.trim(),o=document.getElementById("countryInfo").value,n=document.getElementById("userMobile").value.trim(),d=document.getElementById("userWechat").value.trim(),y=document.getElementById("userEmail").value.trim(),m={name:t,code:o,tel:n,wechat:d,email:y};if(b(m))try{w();const c=await h(m);c.code===200?(l.classList.remove("hidden"),u.reset()):s("注册失败: "+(c.msg||"未知错误"))}catch(c){console.error("请求失败:",c),s("网络错误,请稍后重试")}finally{M()}}function N(){document.getElementById("mobileMenu").classList.toggle("hidden");const t=document.getElementById("menuBtn").querySelector("i");t.classList.contains("fa-bars")?t.classList.replace("fa-bars","fa-times"):t.classList.replace("fa-times","fa-bars")}function H(){const e=document.querySelector("header");window.scrollY>50?(e.classList.add("py-2","shadow"),e.classList.remove("py-3")):(e.classList.add("py-3"),e.classList.remove("py-2","shadow"))}function f(){document.querySelectorAll(".animate-fade-in").forEach(t=>{const o=t.getBoundingClientRect().top,n=window.innerHeight;o<n-100&&(t.style.opacity="1",t.style.transform="translateY(0)")})}window.addEventListener("load",()=>{console.log("页面加载完成,开始调用loadLectureImage..."),v(),f(),document.getElementById("loadingOverlay").style.display="none"});u.addEventListener("submit",A);p.addEventListener("click",()=>{l.classList.add("hidden")});l.addEventListener("click",e=>{e.target===l&&l.classList.add("hidden")});document.getElementById("menuBtn").addEventListener("click",N);window.addEventListener("scroll",H);window.addEventListener("scroll",f); |
|||
import{g as I,r as L}from"./member.js";const w=50,p=100,B=20,u=document.getElementById("registrationForm"),c=document.getElementById("successModal"),v=document.getElementById("closeModal"),l=document.getElementById("submitBtn"),g=document.getElementById("loadingOverlay"),i=document.getElementById("lectureImage");let d=!1;function b(){document.documentElement.style.scrollBehavior="auto";const t=document.getElementById("register");if(t){const e=document.querySelector("header").offsetHeight,n=t.offsetTop-e-20;window.scrollTo(0,n)}setTimeout(()=>{document.documentElement.style.scrollBehavior="smooth"},100)}async function M(){console.log("开始加载讲座图片...");try{const t={id:1};console.log("调用getImageApi,参数:",t);const e=await I(t);console.log("接口返回:",e),e.code===200&&e.data?(i.src=e.data,console.log("讲座图片加载成功:",e.data)):(console.warn("获取图片失败:",e.msg),i.src="https://hc.homilychart.com/hc/250121/img/kecheng.jpg")}catch(t){console.error("加载讲座图片失败:",t),i.src="https://hc.homilychart.com/hc/250121/img/kecheng.jpg"}}function S(){g.style.display="flex",l.disabled=!0,l.textContent="提交中...",d=!0}function A(){g.style.display="none",l.disabled=!1,l.textContent="提交注册",d=!1}function s(t){alert(t)}function T(t){const{name:e,tel:n,email:o}=t;return!e||e.length>w?(s("请输入有效的姓名(最多50个字符)"),document.getElementById("userNameInfo").focus(),!1):!n||n.length>B?(s("请输入有效的电话号码"),document.getElementById("userMobile").focus(),!1):!o||o.length>p?(s("请输入电子邮箱"),document.getElementById("userEmail").focus(),!1):/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(o)?!0:(s("请输入有效的电子邮箱地址"),document.getElementById("userEmail").focus(),!1)}async function H(t){if(t.preventDefault(),d)return;const e=document.getElementById("userNameInfo").value.trim(),n=document.getElementById("countryInfo").value,o=document.getElementById("userMobile").value.trim(),m=document.getElementById("userWechat").value.trim(),y=document.getElementById("userEmail").value.trim(),h=await x(),E=await D(),a={name:e,code:n,tel:o,wechat:m,email:y,type:h,url:E};if(console.log("formData:",a),!!T(a))try{S();const r=await L(a);r.code===200?(c.classList.remove("hidden"),u.reset()):s("注册失败: "+(r.msg||"未知错误"))}catch(r){console.error("请求失败:",r),s("网络错误,请稍后重试")}finally{A()}}function P(){document.getElementById("mobileMenu").classList.toggle("hidden");const e=document.getElementById("menuBtn").querySelector("i");e.classList.contains("fa-bars")?e.classList.replace("fa-bars","fa-times"):e.classList.replace("fa-times","fa-bars")}function N(){const t=document.querySelector("header");window.scrollY>50?(t.classList.add("py-2","shadow"),t.classList.remove("py-3")):(t.classList.add("py-3"),t.classList.remove("py-2","shadow"))}function f(){document.querySelectorAll(".animate-fade-in").forEach(e=>{const n=e.getBoundingClientRect().top,o=window.innerHeight;n<o-100&&(e.style.opacity="1",e.style.transform="translateY(0)")})}document.addEventListener("DOMContentLoaded",function(){b(),setTimeout(()=>{document.body.classList.add("content-visible")},50)});window.addEventListener("load",()=>{console.log("页面加载完成,开始调用loadLectureImage..."),M(),f(),document.getElementById("loadingOverlay").style.display="none"});u.addEventListener("submit",H);v.addEventListener("click",()=>{c.classList.add("hidden")});c.addEventListener("click",t=>{t.target===c&&c.classList.add("hidden")});document.getElementById("menuBtn").addEventListener("click",P);window.addEventListener("scroll",N);window.addEventListener("scroll",f);function x(){const e=new URLSearchParams(window.location.search).get("type");return e==="video"?1:e==="live"?2:e==="article"?3:0}function D(){return new URLSearchParams(window.location.search).get("url")} |
|||
@ -1,18 +1,23 @@ |
|||
import{u as w,e as B,a as E}from"./member.js";let o={pageSize:20,currentPage:1,total:0,items:[]};const f=document.getElementById("tableBody"),h=document.getElementById("pagination"),P=document.getElementById("totalCount");document.getElementById("currentPage");document.getElementById("showingRange");const m=document.getElementById("pageSizeSelect"),v=document.getElementById("gotoInput"),y=document.getElementById("noteModal"),l=document.getElementById("noteTextarea"),x=document.getElementById("noteCancelBtn"),M=document.getElementById("noteSaveBtn"),g=document.getElementById("toast");let u=null;function b(a){g.textContent=a,g.classList.add("show"),setTimeout(()=>g.classList.remove("show"),1e3)}async function I(a,e){try{const t=await E({page:a,page_size:e});if(typeof t.code<"u"&&t.code!==200)throw new Error(t.msg||"接口返回错误");console.log(t);const n=t.data||{};o.items=n.list,o.total=n.total}catch{alert("获取数据失败"),o.total=0}}function S(){const a=(o.currentPage-1)*o.pageSize,e=o.items||[];f.innerHTML=e.map((t,n)=>{const s=a+n+1,c=t.isRelated?"status-1":"status-0",d=t.isRelated?"已联系":"未联系";return`
|
|||
import{u as B,e as L,a as E}from"./member.js";function r(){const n=localStorage.getItem("isLoggedIn")==="true",e=parseInt(localStorage.getItem("loginTime")),a=(new Date().getTime()-e)/(1e3*60*60);return!n||a>=24?(localStorage.removeItem("isLoggedIn"),localStorage.removeItem("loginTime"),!1):!0}function d(){const n=document.getElementById("loginCheckModal");n.style.display="flex"}function $(){window.location.href="login-management.html"}r()||d();document.getElementById("goToLoginBtn").addEventListener("click",$);let o={pageSize:20,currentPage:1,total:0,items:[]};const y=document.getElementById("tableBody"),w=document.getElementById("pagination"),v=document.getElementById("totalCount");document.getElementById("currentPage");document.getElementById("showingRange");const h=document.getElementById("pageSizeSelect"),x=document.getElementById("gotoInput"),b=document.getElementById("noteModal"),u=document.getElementById("noteTextarea"),M=document.getElementById("noteCancelBtn"),P=document.getElementById("noteSaveBtn"),f=document.getElementById("toast");let p=null;function I(n){f.textContent=n,f.classList.add("show"),setTimeout(()=>f.classList.remove("show"),1e3)}async function C(n,e){if(!r()){d();return}try{const t=await E({page:n,page_size:e});if(typeof t.code<"u"&&t.code!==200)throw new Error(t.msg||"接口返回错误");console.log(t);const a=t.data||{};o.items=a.list,o.total=a.total}catch{alert("获取数据失败"),o.total=0}}function S(){const n=(o.currentPage-1)*o.pageSize,e=o.items||[];y.innerHTML=e.map((t,a)=>{const s=n+a+1,c=t.isRelated?"status-1":"status-0",l=t.isRelated?"已联系":"未联系";return(t.code||"").replace(/\+/g,"")+(t.telephone||""),`
|
|||
<tr> |
|||
<td>${s}</td> |
|||
<td>${i(t.name||"")}</td> |
|||
<td> |
|||
<button class="btn whatsapp" data-action="whatsapp" data-id="${t.id}">WhatsApp</button> |
|||
</td> |
|||
<td>${i(t.code||"")}</td> |
|||
<td>${i(t.telephone||"")}</td> |
|||
<td>${i(t.wechat||"")}</td> |
|||
<td>${i(t.email||"")}</td> |
|||
<td>${i(t.type===0?"其它":t.type===1?"视频":t.type===2?"直播":t.type===3?"帖子":"其它")}</td> |
|||
<td>${i(t.url||"")}</td> |
|||
<td>${i(t.createdAt||"")}</td> |
|||
<td> |
|||
<button class="status-btn ${c}" data-action="toggle" data-id="${t.id}">${d}</button> |
|||
<button class="status-btn ${c}" data-action="toggle" data-id="${t.id}">${l}</button> |
|||
</td> |
|||
<td>${i(t.note||"")}</td> |
|||
<td> |
|||
<button class="btn" data-action="editNote" data-id="${t.id}">编辑备注</button> |
|||
</td> |
|||
</tr> |
|||
`}).join(""),P.textContent=o.total}f.addEventListener("click",async a=>{const e=a.target.closest('[data-action="toggle"]');if(e){const n=e.getAttribute("data-id");await $(n,e);return}const t=a.target.closest('[data-action="editNote"]');if(t){const n=t.getAttribute("data-id");L(n);return}});async function $(a,e){const t=o.items.find(c=>String(c.id)===String(a));if(!t)return;const n=t.isRelated,s=n?0:1;t.isRelated=s,e.textContent=s?"已联系":"未联系",e.classList.toggle("status-1",!!s),e.classList.toggle("status-0",!s);try{const c=await w({id:t.id,state:s});if(typeof c.code<"u"&&c.code!==200)throw new Error(c.msg||"更新失败");b("状态修改成功")}catch{t.isRelated=n,e.textContent=n?"已联系":"未联系",e.classList.toggle("status-1",!!n),e.classList.toggle("status-0",!n),alert("更新失败")}}function L(a){const e=o.items.find(t=>String(t.id)===String(a));e&&(u=a,l.value=e.note||"",y.style.display="flex",l.focus())}function r(){u=null,l.value="",y.style.display="none"}x.addEventListener("click",r);M.addEventListener("click",async()=>{if(!u)return r();const a=l.value.trim(),e=o.items.find(t=>String(t.id)===String(u));if(!e)return r();try{const t=await B({id:e.id,note:a});if(typeof t.code<"u"&&t.code!==200)throw new Error(t.msg||"保存失败");const n=e.note;e.note=a,S(),b("备注保存成功")}catch{alert("保存备注失败")}finally{r()}});function z(){const a=Math.max(1,Math.ceil(o.total/o.pageSize)),e=Math.min(Math.max(1,o.currentPage),a);o.currentPage=e;const t=N(e,a,2);let n="";n+=`<button class="btn" data-action="prev" ${e===1?"disabled":""}>上一页</button>`,t.forEach(s=>{s==="..."?n+='<span class="small" style="padding:6px 8px">...</span>':n+=`<button class="btn ${s===e?"active":""}" data-page="${s}">${s}</button>`}),n+=`<button class="btn" data-action="next" ${e===a?"disabled":""}>下一页</button>`,h.innerHTML=n}function N(a,e,t){const n=[],s=Math.max(1,a-t),c=Math.min(e,a+t);s>1&&(n.push(1),s>2&&n.push("..."));for(let d=s;d<=c;d++)n.push(d);return c<e&&(c<e-1&&n.push("..."),n.push(e)),n}h.addEventListener("click",a=>{const e=a.target.closest("button");if(!e)return;const t=e.getAttribute("data-action");if(t==="prev")o.currentPage>1&&o.currentPage--;else if(t==="next"){const n=Math.max(1,Math.ceil(o.total/o.pageSize));o.currentPage<n&&o.currentPage++}else{const n=Number(e.getAttribute("data-page"));isNaN(n)||(o.currentPage=n)}p()});m.addEventListener("change",()=>{const a=parseInt(m.value,10);o.pageSize=a,o.currentPage=1,p(),v.value=""});async function p(){const a=Math.max(1,o.currentPage);o.currentPage=a,await I(o.currentPage,o.pageSize),S(),z()}function i(a){return String(a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}p(); |
|||
`}).join(""),v.textContent=o.total}y.addEventListener("click",async n=>{if(!r()){d();return}const e=n.target.closest('[data-action="whatsapp"]');if(e){const s=e.getAttribute("data-id");T(s);return}const t=n.target.closest('[data-action="toggle"]');if(t){const s=t.getAttribute("data-id");await A(s,t);return}const a=n.target.closest('[data-action="editNote"]');if(a){const s=a.getAttribute("data-id");z(s);return}});function T(n){const e=o.items.find(c=>String(c.id)===String(n));if(!e)return;const a=(e.code||"").replace(/\+/g,"")+(e.telephone||""),s=`https://api.whatsapp.com/send?phone=${encodeURIComponent(a)}&text=${encodeURIComponent("hello欢迎来到赢在美股")}`;window.open(s,"_blank")}async function A(n,e){const t=o.items.find(c=>String(c.id)===String(n));if(!t)return;const a=t.isRelated,s=a?0:1;t.isRelated=s,e.textContent=s?"已联系":"未联系",e.classList.toggle("status-1",!!s),e.classList.toggle("status-0",!s);try{const c=await B({id:t.id,state:s});if(typeof c.code<"u"&&c.code!==200)throw new Error(c.msg||"更新失败");I("状态修改成功")}catch{t.isRelated=a,e.textContent=a?"已联系":"未联系",e.classList.toggle("status-1",!!a),e.classList.toggle("status-0",!a),alert("更新失败")}}function z(n){if(!r()){d();return}const e=o.items.find(t=>String(t.id)===String(n));e&&(p=n,u.value=e.note||"",b.style.display="flex",u.focus())}function g(){p=null,u.value="",b.style.display="none"}M.addEventListener("click",g);P.addEventListener("click",async()=>{if(!r()){d();return}if(!p)return g();const n=u.value.trim(),e=o.items.find(t=>String(t.id)===String(p));if(!e)return g();try{const t=await L({id:e.id,note:n});if(typeof t.code<"u"&&t.code!==200)throw new Error(t.msg||"保存失败");const a=e.note;e.note=n,S(),I("备注保存成功")}catch{alert("保存备注失败")}finally{g()}});function N(){const n=Math.max(1,Math.ceil(o.total/o.pageSize)),e=Math.min(Math.max(1,o.currentPage),n);o.currentPage=e;const t=k(e,n,2);let a="";a+=`<button class="btn" data-action="prev" ${e===1?"disabled":""}>上一页</button>`,t.forEach(s=>{s==="..."?a+='<span class="small" style="padding:6px 8px">...</span>':a+=`<button class="btn ${s===e?"active":""}" data-page="${s}">${s}</button>`}),a+=`<button class="btn" data-action="next" ${e===n?"disabled":""}>下一页</button>`,w.innerHTML=a}function k(n,e,t){const a=[],s=Math.max(1,n-t),c=Math.min(e,n+t);s>1&&(a.push(1),s>2&&a.push("..."));for(let l=s;l<=c;l++)a.push(l);return c<e&&(c<e-1&&a.push("..."),a.push(e)),a}w.addEventListener("click",n=>{if(!r()){d();return}const e=n.target.closest("button");if(!e)return;const t=e.getAttribute("data-action");if(t==="prev")o.currentPage>1&&o.currentPage--;else if(t==="next"){const a=Math.max(1,Math.ceil(o.total/o.pageSize));o.currentPage<a&&o.currentPage++}else{const a=Number(e.getAttribute("data-page"));isNaN(a)||(o.currentPage=a)}m()});h.addEventListener("change",()=>{if(!r()){d();return}const n=parseInt(h.value,10);o.pageSize=n,o.currentPage=1,m(),x.value=""});async function m(){if(!r()){d();return}const n=Math.max(1,o.currentPage);o.currentPage=n,await C(o.currentPage,o.pageSize),S(),N()}function i(n){return String(n).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}r()?m():d();
|
|||
1031
hcdbqb-management.html
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,351 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="zh-CN"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<title>系统登录</title> |
|||
<style> |
|||
* { |
|||
margin: 0; |
|||
padding: 0; |
|||
box-sizing: border-box; |
|||
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif; |
|||
} |
|||
|
|||
body { |
|||
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%); |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
min-height: 100vh; |
|||
padding: 20px; |
|||
} |
|||
|
|||
.login-container { |
|||
background-color: white; |
|||
border-radius: 12px; |
|||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); |
|||
width: 100%; |
|||
max-width: 420px; |
|||
padding: 40px 30px; |
|||
transition: transform 0.3s ease; |
|||
} |
|||
|
|||
.login-container:hover { |
|||
transform: translateY(-5px); |
|||
} |
|||
|
|||
.login-header { |
|||
text-align: center; |
|||
margin-bottom: 30px; |
|||
} |
|||
|
|||
.login-header h1 { |
|||
color: #333; |
|||
font-size: 28px; |
|||
margin-bottom: 8px; |
|||
} |
|||
|
|||
.login-header p { |
|||
color: #666; |
|||
font-size: 16px; |
|||
} |
|||
|
|||
.form-group { |
|||
margin-bottom: 20px; |
|||
} |
|||
|
|||
label { |
|||
display: block; |
|||
margin-bottom: 8px; |
|||
color: #555; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
input { |
|||
width: 100%; |
|||
padding: 14px 16px; |
|||
border: 1px solid #ddd; |
|||
border-radius: 8px; |
|||
font-size: 16px; |
|||
transition: all 0.3s; |
|||
} |
|||
|
|||
input:focus { |
|||
border-color: #4a90e2; |
|||
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.2); |
|||
outline: none; |
|||
} |
|||
|
|||
.error-message { |
|||
color: #e74c3c; |
|||
font-size: 14px; |
|||
margin-top: 5px; |
|||
display: none; |
|||
} |
|||
|
|||
.login-button { |
|||
background: linear-gradient(to right, #6a11cb, #2575fc); |
|||
color: white; |
|||
border: none; |
|||
border-radius: 8px; |
|||
padding: 14px; |
|||
font-size: 16px; |
|||
font-weight: 600; |
|||
cursor: pointer; |
|||
width: 100%; |
|||
transition: all 0.3s; |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.login-button:hover { |
|||
background: linear-gradient(to right, #5a0db5, #1c68e8); |
|||
box-shadow: 0 5px 15px rgba(37, 117, 252, 0.4); |
|||
} |
|||
|
|||
.login-button:active { |
|||
transform: scale(0.98); |
|||
} |
|||
|
|||
.login-footer { |
|||
text-align: center; |
|||
margin-top: 25px; |
|||
color: #777; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.alert { |
|||
padding: 12px 16px; |
|||
border-radius: 8px; |
|||
margin-bottom: 20px; |
|||
display: none; |
|||
} |
|||
|
|||
.alert-error { |
|||
background-color: #ffebee; |
|||
color: #c62828; |
|||
border: 1px solid #ffcdd2; |
|||
} |
|||
|
|||
.alert-success { |
|||
background-color: #e8f5e9; |
|||
color: #2e7d32; |
|||
border: 1px solid #c8e6c9; |
|||
} |
|||
|
|||
.password-requirements { |
|||
font-size: 12px; |
|||
color: #777; |
|||
margin-top: 5px; |
|||
} |
|||
|
|||
.input-error { |
|||
border-color: #e74c3c; |
|||
box-shadow: 0 0 0 3px rgba(231, 76, 60, 0.2); |
|||
} |
|||
</style> |
|||
</head> |
|||
|
|||
<body> |
|||
<div class="login-container"> |
|||
<div class="login-header"> |
|||
<h1>系统登录</h1> |
|||
<p>请输入您的账号和密码</p> |
|||
</div> |
|||
|
|||
<div id="usernameAlert" class="alert alert-error"> |
|||
账号不存在,请检查后重试! |
|||
</div> |
|||
|
|||
<div id="passwordAlert" class="alert alert-error"> |
|||
密码错误,请检查后重试! |
|||
</div> |
|||
|
|||
<form id="loginForm"> |
|||
<div class="form-group"> |
|||
<label for="username">账号</label> |
|||
<input type="text" id="username" name="username" placeholder="请输入账号" required> |
|||
<div class="error-message" id="usernameError">账号不能为空</div> |
|||
</div> |
|||
|
|||
<div class="form-group"> |
|||
<label for="password">密码</label> |
|||
<input type="password" id="password" name="password" placeholder="请输入密码" required> |
|||
<div class="error-message" id="passwordError">密码不能为空</div> |
|||
<div class="password-requirements">密码必须为8位数字</div> |
|||
</div> |
|||
|
|||
<button type="submit" class="login-button">登录</button> |
|||
</form> |
|||
</div> |
|||
|
|||
<script> |
|||
document.addEventListener('DOMContentLoaded', function () { |
|||
const loginForm = document.getElementById('loginForm'); |
|||
const usernameInput = document.getElementById('username'); |
|||
const passwordInput = document.getElementById('password'); |
|||
const usernameError = document.getElementById('usernameError'); |
|||
const passwordError = document.getElementById('passwordError'); |
|||
const usernameAlert = document.getElementById('usernameAlert'); |
|||
const passwordAlert = document.getElementById('passwordAlert'); |
|||
|
|||
// 正确的账号和密码 |
|||
const CORRECT_USERNAME = 'admin'; |
|||
const CORRECT_PASSWORD = '20251107'; |
|||
|
|||
// 表单提交事件 |
|||
loginForm.addEventListener('submit', function (e) { |
|||
e.preventDefault(); |
|||
|
|||
// 重置错误状态 |
|||
resetErrors(); |
|||
|
|||
// 获取表单数据 |
|||
const username = usernameInput.value.trim(); |
|||
const password = passwordInput.value.trim(); |
|||
|
|||
// 表单验证 |
|||
let isValid = true; |
|||
|
|||
if (!username) { |
|||
showError(usernameError, '账号不能为空'); |
|||
usernameInput.classList.add('input-error'); |
|||
isValid = false; |
|||
} |
|||
|
|||
if (!password) { |
|||
showError(passwordError, '密码不能为空'); |
|||
passwordInput.classList.add('input-error'); |
|||
isValid = false; |
|||
} else if (!/^\d{8}$/.test(password)) { |
|||
showError(passwordError, '密码必须为8位数字'); |
|||
passwordInput.classList.add('input-error'); |
|||
isValid = false; |
|||
} |
|||
|
|||
// 如果验证通过,尝试登录 |
|||
if (isValid) { |
|||
// 验证账号密码 |
|||
if (username === CORRECT_USERNAME && password === CORRECT_PASSWORD) { |
|||
// 登录成功,保存登录状态 |
|||
localStorage.setItem('isLoggedIn', 'true'); |
|||
localStorage.setItem('loginTime', new Date().getTime()); |
|||
|
|||
// 显示成功消息(短暂显示后跳转) |
|||
showSuccessMessage(); |
|||
|
|||
// 延迟跳转,让用户看到成功消息 |
|||
setTimeout(function () { |
|||
window.location.href = 'hcdbqb-management.html'; |
|||
}, 1000); |
|||
} else { |
|||
// 登录失败,明确提示是账号还是密码错误 |
|||
if (username !== CORRECT_USERNAME) { |
|||
usernameAlert.style.display = 'block'; |
|||
usernameInput.classList.add('input-error'); |
|||
usernameInput.focus(); |
|||
} else { |
|||
passwordAlert.style.display = 'block'; |
|||
passwordInput.classList.add('input-error'); |
|||
passwordInput.value = ''; |
|||
passwordInput.focus(); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
// 实时验证 |
|||
usernameInput.addEventListener('blur', function () { |
|||
if (!this.value.trim()) { |
|||
showError(usernameError, '账号不能为空'); |
|||
this.classList.add('input-error'); |
|||
} else { |
|||
hideError(usernameError); |
|||
this.classList.remove('input-error'); |
|||
} |
|||
}); |
|||
|
|||
passwordInput.addEventListener('blur', function () { |
|||
const password = this.value.trim(); |
|||
if (!password) { |
|||
showError(passwordError, '密码不能为空'); |
|||
this.classList.add('input-error'); |
|||
} else if (!/^\d{8}$/.test(password)) { |
|||
showError(passwordError, '密码必须为8位数字'); |
|||
this.classList.add('input-error'); |
|||
} else { |
|||
hideError(passwordError); |
|||
this.classList.remove('input-error'); |
|||
} |
|||
}); |
|||
|
|||
// 输入时隐藏错误提示 |
|||
usernameInput.addEventListener('input', function () { |
|||
hideError(usernameError); |
|||
hideAlert(usernameAlert); |
|||
this.classList.remove('input-error'); |
|||
}); |
|||
|
|||
passwordInput.addEventListener('input', function () { |
|||
hideError(passwordError); |
|||
hideAlert(passwordAlert); |
|||
this.classList.remove('input-error'); |
|||
}); |
|||
|
|||
// 辅助函数 |
|||
function showError(element, message) { |
|||
element.textContent = message; |
|||
element.style.display = 'block'; |
|||
} |
|||
|
|||
function hideError(element) { |
|||
element.style.display = 'none'; |
|||
} |
|||
|
|||
function hideAlert(alertElement) { |
|||
alertElement.style.display = 'none'; |
|||
} |
|||
|
|||
function resetErrors() { |
|||
hideError(usernameError); |
|||
hideError(passwordError); |
|||
hideAlert(usernameAlert); |
|||
hideAlert(passwordAlert); |
|||
usernameInput.classList.remove('input-error'); |
|||
passwordInput.classList.remove('input-error'); |
|||
} |
|||
|
|||
function showSuccessMessage() { |
|||
// 临时创建一个成功提示 |
|||
const successAlert = document.createElement('div'); |
|||
successAlert.className = 'alert alert-success'; |
|||
successAlert.textContent = '登录成功,正在跳转...'; |
|||
usernameAlert.parentNode.insertBefore(successAlert, usernameAlert); |
|||
successAlert.style.display = 'block'; |
|||
|
|||
// 移除错误提示(如果有) |
|||
hideAlert(usernameAlert); |
|||
hideAlert(passwordAlert); |
|||
} |
|||
|
|||
// 检查是否已经登录(如果已经登录,直接跳转) |
|||
if (localStorage.getItem('isLoggedIn') === 'true') { |
|||
// 检查登录时间,如果超过24小时需要重新登录 |
|||
const loginTime = parseInt(localStorage.getItem('loginTime')); |
|||
const currentTime = new Date().getTime(); |
|||
const hoursDiff = (currentTime - loginTime) / (1000 * 60 * 60); |
|||
|
|||
if (hoursDiff < 24) { |
|||
window.location.href = 'hcdbqb-management.html'; |
|||
} else { |
|||
// 超过24小时,清除登录状态 |
|||
localStorage.removeItem('isLoggedIn'); |
|||
localStorage.removeItem('loginTime'); |
|||
} |
|||
} |
|||
}); |
|||
</script> |
|||
</body> |
|||
|
|||
</html> |
|||
@ -0,0 +1,138 @@ |
|||
// @ts-nocheck
|
|||
export {}; |
|||
|
|||
; declare global { |
|||
var __VLS_PROPS_FALLBACK: Record<string, unknown>; |
|||
|
|||
const __VLS_directiveBindingRestFields: { instance: null, oldValue: null, modifiers: any, dir: any }; |
|||
const __VLS_unref: typeof import('vue').unref; |
|||
const __VLS_placeholder: any; |
|||
const __VLS_intrinsics: import('vue/jsx-runtime').JSX.IntrinsicElements; |
|||
|
|||
type __VLS_Elements = __VLS_SpreadMerge<SVGElementTagNameMap, HTMLElementTagNameMap>; |
|||
type __VLS_GlobalComponents = import('vue').GlobalComponents; |
|||
type __VLS_GlobalDirectives = import('vue').GlobalDirectives; |
|||
type __VLS_IsAny<T> = 0 extends 1 & T ? true : false; |
|||
type __VLS_PickNotAny<A, B> = __VLS_IsAny<A> extends true ? B : A; |
|||
type __VLS_SpreadMerge<A, B> = Omit<A, keyof B> & B; |
|||
type __VLS_WithComponent<N0 extends string, LocalComponents, Self, N1 extends string, N2 extends string, N3 extends string> = |
|||
N1 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N1] } : |
|||
N2 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N2] } : |
|||
N3 extends keyof LocalComponents ? { [K in N0]: LocalComponents[N3] } : |
|||
Self extends object ? { [K in N0]: Self } : |
|||
N1 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N1] } : |
|||
N2 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N2] } : |
|||
N3 extends keyof __VLS_GlobalComponents ? { [K in N0]: __VLS_GlobalComponents[N3] } : |
|||
{}; |
|||
type __VLS_FunctionalComponentCtx<T, K> = __VLS_PickNotAny<'__ctx' extends keyof __VLS_PickNotAny<K, {}> |
|||
? K extends { __ctx?: infer Ctx } ? NonNullable<Ctx> : never : any |
|||
, T extends (props: any, ctx: infer Ctx) => any ? Ctx : any |
|||
>; |
|||
type __VLS_FunctionalComponentProps<T, K> = '__ctx' extends keyof __VLS_PickNotAny<K, {}> |
|||
? K extends { __ctx?: { props?: infer P } } ? NonNullable<P> : never |
|||
: T extends (props: infer P, ...args: any) => any ? P |
|||
: {}; |
|||
type __VLS_FunctionalComponent<T> = (props: (T extends { $props: infer Props } ? Props : {}) & Record<string, unknown>, ctx?: any) => import('vue/jsx-runtime').JSX.Element & { |
|||
__ctx?: { |
|||
attrs?: any; |
|||
slots?: T extends { $slots: infer Slots } ? Slots : Record<string, any>; |
|||
emit?: T extends { $emit: infer Emit } ? Emit : {}; |
|||
props?: typeof props; |
|||
expose?: (exposed: T) => void; |
|||
}; |
|||
}; |
|||
type __VLS_IsFunction<T, K> = K extends keyof T |
|||
? __VLS_IsAny<T[K]> extends false |
|||
? unknown extends T[K] |
|||
? false |
|||
: true |
|||
: false |
|||
: false; |
|||
type __VLS_NormalizeComponentEvent< |
|||
Props, |
|||
Emits, |
|||
onEvent extends keyof Props, |
|||
Event extends keyof Emits, |
|||
CamelizedEvent extends keyof Emits, |
|||
> = __VLS_IsFunction<Props, onEvent> extends true |
|||
? Props |
|||
: __VLS_IsFunction<Emits, Event> extends true |
|||
? { [K in onEvent]?: Emits[Event] } |
|||
: __VLS_IsFunction<Emits, CamelizedEvent> extends true |
|||
? { [K in onEvent]?: Emits[CamelizedEvent] } |
|||
: Props; |
|||
// fix https://github.com/vuejs/language-tools/issues/926
|
|||
type __VLS_UnionToIntersection<U> = (U extends unknown ? (arg: U) => unknown : never) extends ((arg: infer P) => unknown) ? P : never; |
|||
type __VLS_OverloadUnionInner<T, U = unknown> = U & T extends (...args: infer A) => infer R |
|||
? U extends T |
|||
? never |
|||
: __VLS_OverloadUnionInner<T, Pick<T, keyof T> & U & ((...args: A) => R)> | ((...args: A) => R) |
|||
: never; |
|||
type __VLS_OverloadUnion<T> = Exclude< |
|||
__VLS_OverloadUnionInner<(() => never) & T>, |
|||
T extends () => never ? never : () => never |
|||
>; |
|||
type __VLS_ConstructorOverloads<T> = __VLS_OverloadUnion<T> extends infer F |
|||
? F extends (event: infer E, ...args: infer A) => any |
|||
? { [K in E & string]: (...args: A) => void; } |
|||
: never |
|||
: never; |
|||
type __VLS_NormalizeEmits<T> = __VLS_PrettifyGlobal< |
|||
__VLS_UnionToIntersection< |
|||
__VLS_ConstructorOverloads<T> & { |
|||
[K in keyof T]: T[K] extends any[] ? { (...args: T[K]): void } : never |
|||
} |
|||
> |
|||
>; |
|||
type __VLS_EmitsToProps<T> = __VLS_PrettifyGlobal<{ |
|||
[K in string & keyof T as `on${Capitalize<K>}`]?: |
|||
(...args: T[K] extends (...args: infer P) => any ? P : T[K] extends null ? any[] : never) => any; |
|||
}>; |
|||
type __VLS_ResolveEmits< |
|||
Comp, |
|||
Emits, |
|||
TypeEmits = Comp extends { __typeEmits?: infer T } ? unknown extends T ? {} : import('vue').ShortEmitsToObject<T> : {}, |
|||
NormalizedEmits = __VLS_NormalizeEmits<Emits> extends infer E ? string extends keyof E ? {} : E : never, |
|||
> = __VLS_SpreadMerge<NormalizedEmits, TypeEmits>; |
|||
type __VLS_ResolveDirectives<T> = { |
|||
[K in keyof T & string as `v${Capitalize<K>}`]: T[K]; |
|||
}; |
|||
type __VLS_PrettifyGlobal<T> = { [K in keyof T as K]: T[K]; } & {}; |
|||
type __VLS_WithDefaultsGlobal<P, D> = { |
|||
[K in keyof P as K extends keyof D ? K : never]-?: P[K]; |
|||
} & { |
|||
[K in keyof P as K extends keyof D ? never : K]: P[K]; |
|||
}; |
|||
type __VLS_UseTemplateRef<T> = Readonly<import('vue').ShallowRef<T | null>>; |
|||
type __VLS_ProxyRefs<T> = import('vue').ShallowUnwrapRef<T>; |
|||
|
|||
function __VLS_getVForSourceType<T extends number | string | any[] | Iterable<any>>(source: T): [ |
|||
item: T extends number ? number |
|||
: T extends string ? string |
|||
: T extends any[] ? T[number] |
|||
: T extends Iterable<infer T1> ? T1 |
|||
: any, |
|||
index: number, |
|||
][]; |
|||
function __VLS_getVForSourceType<T>(source: T): [ |
|||
item: T[keyof T], |
|||
key: keyof T, |
|||
index: number, |
|||
][]; |
|||
function __VLS_getSlotParameters<S, D extends S>(slot: S, decl?: D): |
|||
D extends (...args: infer P) => any ? P : any[]; |
|||
function __VLS_asFunctionalDirective<T>(dir: T): T extends import('vue').ObjectDirective |
|||
? NonNullable<T['created' | 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated' | 'beforeUnmount' | 'unmounted']> |
|||
: T extends (...args: any) => any |
|||
? T |
|||
: (arg1: unknown, arg2: unknown, arg3: unknown, arg4: unknown) => void; |
|||
function __VLS_asFunctionalComponent<T, K = T extends new (...args: any) => any ? InstanceType<T> : unknown>(t: T, instance?: K): |
|||
T extends new (...args: any) => any ? __VLS_FunctionalComponent<K> |
|||
: T extends () => any ? (props: {}, ctx?: any) => ReturnType<T> |
|||
: T extends (...args: any) => any ? T |
|||
: __VLS_FunctionalComponent<{}>; |
|||
function __VLS_functionalComponentArgsRest<T extends (...args: any) => any>(t: T): 2 extends Parameters<T>['length'] ? [any] : []; |
|||
function __VLS_asFunctionalElement<T>(tag: T, endTag?: T): (attrs: T & Record<string, unknown>) => void; |
|||
function __VLS_asFunctionalSlot<S>(slot: S): S extends () => infer R ? (props: {}) => R : NonNullable<S>; |
|||
function __VLS_tryAsConstant<const T>(t: T): T; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue