Browse Source
1.dnoe split stage; 2.add assets manager; 3.1-2never test; 4.before polling taget bak; and db update;master
28 changed files with 3502 additions and 406 deletions
@ -1,4 +1,4 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<project version="4"> |
|||
<component name="ProjectRootManager" version="2" project-jdk-name="Remote Python 3.8.20 (sftp://root@192.168.3.151:22/root/miniconda3/envs/py38/bin/python)" project-jdk-type="Python SDK" /> |
|||
<component name="ProjectRootManager" version="2" project-jdk-name="Remote Python 3.8.20 (sftp://root@192.168.204.135:22/root/ENTER/envs/py38/bin/python)" project-jdk-type="Python SDK" /> |
|||
</project> |
@ -0,0 +1,178 @@ |
|||
from mycode.DBManager import app_DBM |
|||
|
|||
class AssetsManager: |
|||
def __init__(self): |
|||
pass |
|||
|
|||
def __del__(self): |
|||
pass |
|||
|
|||
def get_IP_assets(self,IP,user,safe_rank): |
|||
ip_assets = [] |
|||
ip_assets = app_DBM.get_ip_assets_db(IP,user,safe_rank) |
|||
if not ip_assets: |
|||
ip_assets = [] |
|||
return ip_assets |
|||
|
|||
def get_IP_info(self,IP): |
|||
ip_info = app_DBM.get_ip_info_db(IP) |
|||
if not ip_info: |
|||
ip_info = [] |
|||
return ip_info |
|||
|
|||
def get_assets_users(self,uname): |
|||
assets_users = app_DBM.get_assets_users_db(uname) |
|||
if not assets_users: |
|||
assets_users = [] |
|||
return assets_users |
|||
|
|||
def update_assets_users(self,IP,owner_id,itype = 1): |
|||
return app_DBM.update_assets_users_db(IP,owner_id,itype) |
|||
|
|||
def get_port_latest(self,ip): |
|||
return app_DBM.get_port_latest_db(ip) |
|||
|
|||
def get_port_history(self,ip): |
|||
#拿ip_id |
|||
strsql = "select id,scan_count from ip_assets where ip_address = %s" |
|||
params = (ip,) |
|||
ip_data = app_DBM.safe_do_select(strsql,params,1) |
|||
ip_id = ip_data[0] |
|||
scan_count = ip_data[1] |
|||
# 1) 拿批次 |
|||
sql_batches = ''' |
|||
SELECT DISTINCT scan_count, scan_time |
|||
FROM port_assets |
|||
WHERE ip_id=%s |
|||
ORDER BY scan_count ASC; |
|||
''' |
|||
batches = app_DBM.safe_do_select(sql_batches, (ip_id,)) |
|||
times = [row[1] for row in batches] |
|||
counts = [row[0] for row in batches] |
|||
if len(times) != scan_count: |
|||
print(f"*****数据批次有问题") |
|||
# 2) 拿所有端口数据 |
|||
sql_ports = ''' |
|||
SELECT port, service, version, status, scan_count |
|||
FROM port_assets |
|||
WHERE ip_id=%s |
|||
ORDER BY port ASC, scan_count ASC; |
|||
''' |
|||
rows = app_DBM.safe_do_select(sql_ports, (ip_id,)) |
|||
# 3) 组织成: { port: { service, version, statuses: [...], changed: [...] } } |
|||
from collections import OrderedDict |
|||
port_dict = OrderedDict() |
|||
for port, service, version, status, sc in rows: |
|||
entry = port_dict.setdefault(port, { |
|||
'service': [], |
|||
'version': [], |
|||
'statuses': [], |
|||
'scancount': [], |
|||
'changed': [] |
|||
}) |
|||
entry['service'].append(service) |
|||
entry['version'].append(version) |
|||
entry['statuses'].append(status) |
|||
entry['scancount'].append(sc) |
|||
# 4) 计算 changed 数组:与前一批次对比 |
|||
for port, info in port_dict.items(): |
|||
service = info['service'] |
|||
version = info['version'] |
|||
statuses = info['statuses'] |
|||
#scancount = info['scancount'] #执行次序不用对比 |
|||
changed = [] |
|||
for i in range(len(service)): |
|||
if i == 0: |
|||
changed.append(0) # 第一个批次,默认无变化 |
|||
else: |
|||
if service[i] != service[i-1] or version[i] != version[i-1] or statuses[i] != statuses[i-1]: |
|||
changed.append(1) |
|||
else: |
|||
changed.append(0) |
|||
info['changed'] = changed |
|||
return times,port_dict |
|||
|
|||
def get_ip_url_latest(self,ip): |
|||
return app_DBM.get_ip_url_latest_db(ip) |
|||
|
|||
def get_ip_url_history(self,ip): |
|||
return app_DBM.get_ip_url_history_db(ip) |
|||
|
|||
def get_vul_data(self,ip,nodeName,vulType,vulLevel): |
|||
# 先获取该IP最新的task_id |
|||
task_id = app_DBM.get_last_task_by_ip(ip) |
|||
if not task_id: |
|||
return [] |
|||
vuls = app_DBM.get_task_vul(task_id, nodeName, vulType, vulLevel) |
|||
return vuls |
|||
|
|||
def del_ip_assets(self,ip): |
|||
bsuccess,error = app_DBM.del_ip_assets(ip) |
|||
return bsuccess,error |
|||
|
|||
def get_url_assets(self,url,owner,email): |
|||
url_assets = app_DBM.get_url_assets_db(url,owner,email) |
|||
return url_assets |
|||
|
|||
def get_url_to_ip(self,url_id): |
|||
last_to_ips,his_to_ip = app_DBM.get_url_to_ip_db(url_id) |
|||
return last_to_ips,his_to_ip |
|||
|
|||
def del_url_assets(self,url_id): |
|||
bsuccess, error = app_DBM.del_url_assets_db(url_id) |
|||
return bsuccess, error |
|||
|
|||
def get_owners(self,owner, owner_type, contact, tellnum): |
|||
owner_list = [] |
|||
owner_list = app_DBM.get_owner_db(owner, owner_type, contact, tellnum) |
|||
return owner_list |
|||
|
|||
def add_update_owner(self,owner_data, do_mode): |
|||
''' |
|||
:param owner_data: |
|||
:param do_mode: |
|||
:return: |
|||
''' |
|||
id = owner_data["id"] |
|||
user = owner_data["user"] |
|||
type = owner_data["type"] |
|||
contact = owner_data["contact"] |
|||
phone = owner_data["phone"] |
|||
IDno = owner_data["IOno"] |
|||
if not user or not type or not contact or not phone or not IDno: |
|||
return False, "有信息没有填写,请补充完整!", [] |
|||
if do_mode =="add": |
|||
strsql = "select ID from assets_user where ID_num = %s" |
|||
params = (IDno,) |
|||
data = app_DBM.safe_do_select(strsql, params, 1) |
|||
if data: |
|||
return False, "证件号码已经存在,请重新修改", [] |
|||
|
|||
strsql = "insert into assets_user (itype,uname,tellnum,tell_username,ID_num) values (%s,%s,%s,%s,%s);" |
|||
params = (type,user,phone,contact,IDno) |
|||
bok,new_id = app_DBM.safe_do_sql(strsql,params,1) |
|||
elif do_mode == "edit": |
|||
strsql = "select ID from assets_user where ID_num = %s and ID <> %s" |
|||
params = (IDno,id) |
|||
data = app_DBM.safe_do_select(strsql, params, 1) |
|||
if data: |
|||
return False, "证件号码已经存在,请重新修改", [] |
|||
|
|||
strsql = "update assets_user set itype=%s,uname=%s,tellnum=%s,tell_username=%s,ID_num=%s where ID=%s;" |
|||
params = (type, user, phone, contact, IDno,id) |
|||
bok,_ = app_DBM.safe_do_sql(strsql,params) |
|||
else: |
|||
return False,"操作模式超出预期",[] |
|||
|
|||
if bok: |
|||
owner_list = app_DBM.get_owner_db("", "", "", "") |
|||
return True,"",owner_list |
|||
else: |
|||
return False, "数据库操作失败", [] |
|||
|
|||
def del_owner(self,id): |
|||
bsuccess,error = app_DBM.del_owner_db(id) |
|||
return bsuccess,error |
|||
|
|||
|
|||
g_AssetsM = AssetsManager() |
@ -1,4 +1,4 @@ |
|||
from quart import Blueprint |
|||
#定义模块 |
|||
api = Blueprint('api',__name__) |
|||
from . import user,task,wsm,system |
|||
from . import user,task,wsm,system,assets |
|||
|
@ -0,0 +1,170 @@ |
|||
from . import api |
|||
from quart import Quart, render_template, redirect, url_for, request,jsonify |
|||
from mycode.AssetsManager import g_AssetsM |
|||
from web.common.utils import login_required |
|||
|
|||
@api.route('/assets/getipassets',methods=['POST']) |
|||
@login_required |
|||
async def get_IP_assets(): #查询IP资产数据 |
|||
data = await request.get_json() |
|||
IP = data.get("IP") |
|||
user = data.get("user") |
|||
safe_rank = data.get("safe_rank") |
|||
ip_assets = g_AssetsM.get_IP_assets(IP,user,safe_rank) |
|||
return jsonify({"ip_assets":ip_assets}) |
|||
|
|||
@api.route('/assets/getipinfo',methods=['POST']) |
|||
@login_required |
|||
async def get_IP_info(): #获取该IP资产的基本信息 |
|||
data = await request.get_json() |
|||
IP = data.get("IP") |
|||
ip_info = g_AssetsM.get_IP_info(IP) |
|||
return jsonify({"ip_info":ip_info}) |
|||
|
|||
@api.route('/assets/getassetsuser',methods=['POST']) |
|||
@login_required |
|||
async def get_assets_user(): |
|||
data = await request.get_json() |
|||
uname = data.get("keyword") |
|||
user_list = g_AssetsM.get_assets_users(uname) |
|||
return jsonify({"user_list": user_list}) |
|||
|
|||
@api.route('/assets/updateipinfo',methods=['POST']) |
|||
@login_required |
|||
async def update_IP_info(): #获取该IP资产的基本信息 |
|||
data = await request.get_json() |
|||
IP = data.get("cur_ip") |
|||
owner_id = data.get("owner_id") |
|||
bsuccess,error = g_AssetsM.update_assets_users(IP,owner_id) |
|||
return jsonify({"bsuccess":bsuccess}) |
|||
|
|||
@api.route('/assets/getportlatest',methods=['POST']) |
|||
@login_required |
|||
async def get_port_latest(): #获取端口的最新数据 |
|||
data = await request.get_json() |
|||
ip = data.get("ip") |
|||
if not ip: |
|||
return jsonify({'error': '缺少 ip 参数'}), 400 |
|||
port_data = g_AssetsM.get_port_latest(ip) |
|||
return jsonify({"port_data":port_data}) |
|||
|
|||
@api.route('/assets/getporthistory',methods=['POST']) |
|||
@login_required |
|||
async def get_port_history(): #获取端口的历史数据 |
|||
data = await request.get_json() |
|||
ip = data.get("ip") |
|||
if not ip: |
|||
return jsonify({'error': '缺少 ip 参数'}), 400 |
|||
times,port_dict = g_AssetsM.get_port_history(ip) |
|||
return jsonify({"times":times,"entries":port_dict}) |
|||
|
|||
@api.route('/assets/geturllatest',methods=['POST']) |
|||
@login_required |
|||
async def get_url_latest(): |
|||
data = await request.get_json() |
|||
ip = data.get("ip") |
|||
if not ip: |
|||
return jsonify({'error': '缺少 ip 参数'}), 400 |
|||
ip_url_data = g_AssetsM.get_ip_url_latest(ip) |
|||
if not ip_url_data: |
|||
ip_url_data=[] |
|||
return jsonify({"ip_url_data": ip_url_data}) |
|||
|
|||
@api.route('/assets/geturlhistory',methods=['POST']) |
|||
@login_required |
|||
async def get_url_history(): |
|||
data = await request.get_json() |
|||
ip = data.get("ip") |
|||
if not ip: |
|||
return jsonify({'error': '缺少 ip 参数'}), 400 |
|||
ip_url_data = g_AssetsM.get_ip_url_history(ip) |
|||
if not ip_url_data: |
|||
ip_url_data=[] |
|||
return jsonify({"ip_url_data": ip_url_data}) |
|||
|
|||
@api.route('/assets/getvuldata',methods=['POST']) |
|||
@login_required |
|||
async def get_vul_data(): |
|||
data = await request.get_json() #ip,nodeName,vulType,vulLevel |
|||
ip = data.get("ip") |
|||
if not ip: |
|||
return jsonify({'error': '缺少 ip 参数'}), 400 |
|||
nodeName = data.get("nodeName") |
|||
vulType = data.get("vulType") |
|||
vulLevel = data.get("vulLevel") |
|||
vuls = g_AssetsM.get_vul_data(ip,nodeName,vulType,vulLevel) |
|||
return jsonify({"vuls": vuls}) |
|||
|
|||
|
|||
@api.route('/assets/delipassets',methods=['POST']) |
|||
@login_required |
|||
async def del_ip_assets(): |
|||
data = await request.get_json() # ip,nodeName,vulType,vulLevel |
|||
ip = data.get("IP") |
|||
if not ip: |
|||
return jsonify({'error': '缺少 ip 参数'}), 400 |
|||
bsuccess,error = g_AssetsM.del_ip_assets(ip) |
|||
return jsonify({"bsuccess": bsuccess,"error":error}) |
|||
|
|||
@api.route('/assets/geturlassets', methods=['POST']) |
|||
@login_required |
|||
async def get_url_assets(): #url_filter,user_filter,email_filter |
|||
data = await request.get_json() |
|||
url = data.get("url_filter") |
|||
owner = data.get("user_filter") |
|||
email = data.get("email_filter") |
|||
url_assets = g_AssetsM.get_url_assets(url,owner,email) |
|||
return jsonify({"url_assets": url_assets}) |
|||
|
|||
@api.route('/assets/updateurlinfo', methods=['POST']) |
|||
@login_required |
|||
async def update_url_info(): |
|||
data = await request.get_json() |
|||
url_id = data.get("cur_url_id") |
|||
owner_id = data.get("owner_id") |
|||
bsuccess, error = g_AssetsM.update_assets_users(url_id, owner_id,2) |
|||
return jsonify({"bsuccess": bsuccess,"error":error}) |
|||
|
|||
@api.route('/assets/geturltoIP', methods=['POST']) |
|||
@login_required |
|||
async def get_url_to_ip(): |
|||
data = await request.get_json() |
|||
url_id = data.get("cur_url_id") |
|||
last_to_ips,his_to_ips = g_AssetsM.get_url_to_ip(url_id) |
|||
return jsonify({"last_to_ips":last_to_ips,"his_to_ips":his_to_ips}) |
|||
|
|||
@api.route('/assets/delurlassets', methods=['POST']) |
|||
@login_required |
|||
async def del_url_assets(): |
|||
data = await request.get_json() |
|||
url_id = data.get("url") |
|||
bsuccess,error = g_AssetsM.del_url_assets(url_id) |
|||
return jsonify({"bsuccess": bsuccess, "error": error}) |
|||
|
|||
@api.route('/assets/getOwners', methods=['POST']) |
|||
@login_required |
|||
async def getOwners(): |
|||
data = await request.get_json() |
|||
owner = data.get("owner") |
|||
owner_type = data.get("owner_type") |
|||
contact = data.get("contact") |
|||
tellnum = data.get("tellnum") |
|||
owner_list = g_AssetsM.get_owners(owner,owner_type,contact,tellnum) |
|||
return jsonify({"owner_list": owner_list}) |
|||
|
|||
@api.route('/assets/addUpdateOwners', methods=['POST']) |
|||
@login_required |
|||
async def add_update_Owner(): |
|||
data = await request.get_json() #{data,currentMode} |
|||
owner_data = data.get("data") |
|||
do_mode = data.get("currentMode") |
|||
bsuccess,error,owner_list = g_AssetsM.add_update_owner(owner_data, do_mode) |
|||
return jsonify({"owner_list": owner_list,"bsuccess":bsuccess,"error":error}) |
|||
|
|||
@api.route('/assets/delOwners', methods=['POST']) |
|||
@login_required |
|||
async def del_Owner(): |
|||
data = await request.get_json() |
|||
id = data.get("id") |
|||
bsuccess,error = g_AssetsM.del_owner(id) |
|||
return jsonify({"bsuccess": bsuccess, "error": error}) |
File diff suppressed because it is too large
@ -0,0 +1,10 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>Title</title> |
|||
</head> |
|||
<body> |
|||
|
|||
</body> |
|||
</html> |
@ -0,0 +1,331 @@ |
|||
{% extends 'base.html' %} |
|||
|
|||
{% block title %}ZFSAFE{% endblock %} |
|||
|
|||
<!-- 页面样式块 --> |
|||
{% block style %} |
|||
/* 查询条件区域:使用 row 分布,输入框占满所在列 */ |
|||
.search-section .form-control, |
|||
.search-section .form-select { |
|||
width: 100%; |
|||
} |
|||
/* 查询条件区域,每个条件统一高度且左右间隔均等 */ |
|||
.search-section .col { |
|||
padding: 0 5px; |
|||
} |
|||
|
|||
/* 表格样式:统一垂直居中 */ |
|||
.table thead th, .table tbody td { |
|||
vertical-align: middle; |
|||
text-align: center; |
|||
} |
|||
|
|||
/* 分页区域右对齐 */ |
|||
.pagination-section { |
|||
text-align: right; |
|||
padding-right: 15px; |
|||
} |
|||
|
|||
/* 固定行高,比如 45px,每页 10 行 */ |
|||
.fixed-row-height { |
|||
height: 45px; |
|||
overflow: hidden; |
|||
} |
|||
{% endblock %} |
|||
|
|||
<!-- 页面内容块 --> |
|||
{% block content %} |
|||
<div class="container-xxl mt-2"> |
|||
<!-- 查询区 --> |
|||
<div class="row mb-3"> |
|||
<div class="col-2 mb-2"><button class="btn btn-primary me-3" onclick="openModal('add')">新增</button></div> |
|||
<div class="col-10"></div> |
|||
<div class="col-3 mb-2"><input type="text" class="form-control" id="searchUser" placeholder="资产用户"></div> |
|||
<div class="col-2"> |
|||
<select class="form-select" id="ownerType"> |
|||
<option value="">用户类型</option> |
|||
<option value="个人">个人</option> |
|||
<option value="私营企业">私营企业</option> |
|||
<option value="国有企业">国有企业</option> |
|||
<option value="事业单位">事业单位</option> |
|||
<option value="政府机构">政府机构</option> |
|||
<option value="团体协会">团体协会</option> |
|||
</select> |
|||
</div> |
|||
<div class="col-2"><input type="text" class="form-control" id="searchContact" placeholder="联系人"></div> |
|||
<div class="col-3"><input type="text" class="form-control" id="searchPhone" placeholder="联系电话"></div> |
|||
<div class="col-2 text-end"> |
|||
<button class="btn btn-primary" onclick="fetchData()">查询</button> |
|||
<button class="btn btn-primary" onclick="exportOwnerData()">导出</button> |
|||
</div> |
|||
</div> |
|||
<!-- 表格 --> |
|||
<div class="table-responsive"> |
|||
<table class="table table-bordered table-hover" id="ownerTable" style="width: 100%; table-layout: fixed;"> |
|||
<thead> |
|||
<tr> |
|||
<th style="width:5%;">序号</th> |
|||
<th style="width:30%;">资产用户</th> |
|||
<th style="width:10%;">用户类型</th> |
|||
<th style="width:10%;">联系人</th> |
|||
<th style="width:15%;">联系电话</th> |
|||
<th style="width:10%;">关联资产</th> |
|||
<th style="width:20%;">操作</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<!-- JS 动态插入 10 行 --> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<!-- 分页 --> |
|||
<nav class="mt-2"> |
|||
<ul class="pagination justify-content-end" id="userPagination"> |
|||
<li class="page-item"><a class="page-link" href="#" id="userPrev">上一页</a></li> |
|||
<li class="page-item"><a class="page-link" href="#" id="userNext">下一页</a></li> |
|||
</ul> |
|||
</nav> |
|||
|
|||
</div> |
|||
|
|||
<!-- Modal --> |
|||
<div class="modal fade" id="ownerModal" tabindex="-1" aria-hidden="true"> |
|||
<div class="modal-dialog"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<h5 class="modal-title" id="modalTitle">新增/修改用户</h5> |
|||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button> |
|||
</div> |
|||
<div class="modal-body"> |
|||
<form id="ownerForm"> |
|||
<input type="hidden" id="ownerId"> |
|||
<div class="mb-3"> |
|||
<label class="form-label me-2">资产用户:</label> |
|||
<input type="text" class="form-control" id="formUser"> |
|||
</div> |
|||
<div class="mb-3"> |
|||
<label class="form-label me-2">用户类型:</label> |
|||
<select class="form-select" id="formType"> |
|||
<option value="">用户类型</option> |
|||
<option value="私营企业">私营企业</option> |
|||
<option value="国有企业">国有企业</option> |
|||
<option value="事业单位">事业单位</option> |
|||
<option value="政府机构">政府机构</option> |
|||
<option value="团体协会">团体协会</option> |
|||
<option value="个人">个人</option> |
|||
</select> |
|||
</div> |
|||
<div class="mb-3"> |
|||
<label class="form-label me-2">证件号码:</label> |
|||
<input type="text" class="form-control" id="IDno"> |
|||
</div> |
|||
<div class="mb-1 row gx-3"> |
|||
<div class="col-6"> |
|||
<label class="form-label me-2">联系人:</label> |
|||
<input type="text" class="form-control" id="formContact"> |
|||
</div> |
|||
<div class="col-6"> |
|||
<label class="form-label me-2">联系电话:</label> |
|||
<input type="text" class="form-control" id="formPhone"> |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
<div class="modal-footer"> |
|||
<button class="btn btn-primary" onclick="saveOwner()">保存</button> |
|||
<button class="btn btn-secondary" data-bs-dismiss="modal">关闭</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endblock %} |
|||
|
|||
<!-- 页面脚本块 --> |
|||
{% block script %} |
|||
<script> |
|||
let currentMode = 'add'; |
|||
let ownersData = []; |
|||
let currentPage = 1; |
|||
const pageSize = 10; |
|||
async function postJSON(url, payload) { |
|||
const res = await fetch(url, { |
|||
method: 'POST', |
|||
headers: { 'Content-Type':'application/json' }, |
|||
body: JSON.stringify(payload) |
|||
}); |
|||
if (!res.ok) { |
|||
const errorData = await res.json(); |
|||
throw new Error(errorData.error || `HTTP错误 ${res.status}`); |
|||
} |
|||
return res.json(); |
|||
} |
|||
|
|||
function downloadCSV(text, filename) { |
|||
const blob = new Blob([text], { type: 'text/csv;charset=utf-8;' }); |
|||
const link = document.createElement('a'); |
|||
link.href = URL.createObjectURL(blob); |
|||
link.download = filename; |
|||
link.click(); |
|||
URL.revokeObjectURL(link.href); |
|||
} |
|||
|
|||
function fmtCell(val) { |
|||
// 如果是数字-数字,比如 "2-4"、"10-12" 等 |
|||
if (/^\d+-\d+$/.test(val)) { |
|||
return '="' + val + '"'; |
|||
} |
|||
// 如果里面有中文或逗号,就双引号包裹 |
|||
if (/[,\u4e00-\u9fa5]/.test(val)) { |
|||
return `"${val.replace(/"/g, '""')}"`; |
|||
} |
|||
return val; |
|||
} |
|||
|
|||
async function exportOwnerData(){ |
|||
//导出数据 |
|||
let rows = [ |
|||
['序号', '资产用户', '用户类型', '联系人', '联系电话','证件号码','关联IP','关联域名'].map(fmtCell).join(',') |
|||
]; |
|||
ownersData.forEach((row, i) => { |
|||
rows.push([ |
|||
(i + 1).toString(), |
|||
row[2].toString(), |
|||
row[1] || '', |
|||
row[4] || '', |
|||
row[3] || '', |
|||
row[5] || '', |
|||
row[6].toString(), |
|||
row[7].toString(), |
|||
].map(fmtCell).join(',')); |
|||
}); |
|||
|
|||
const csv = '\uFEFF' + rows.join('\r\n'); // 加 BOM |
|||
|
|||
downloadCSV(csv, `assets_owner.csv`); |
|||
} |
|||
|
|||
function openModal(mode, index = null) { |
|||
currentMode = mode; |
|||
document.getElementById('ownerForm').reset(); |
|||
document.getElementById('ownerId').value = ''; |
|||
if (mode === 'edit' && index !== null) { |
|||
const data = ownersData[index]; |
|||
document.getElementById('modalTitle').innerText = '修改用户'; |
|||
document.getElementById('ownerId').value = data[0] || ''; |
|||
document.getElementById('formUser').value = data[2] || ''; |
|||
document.getElementById('formType').value = data[1] || ''; |
|||
document.getElementById('formContact').value = data[4] || ''; |
|||
document.getElementById('formPhone').value = data[3] || ''; |
|||
document.getElementById('IDno').value = data[5] || ''; |
|||
} else { |
|||
document.getElementById('modalTitle').innerText = '新增用户'; |
|||
} |
|||
new bootstrap.Modal(document.getElementById('ownerModal')).show(); |
|||
} |
|||
|
|||
async function saveOwner() { |
|||
const data = { |
|||
id: document.getElementById('ownerId').value, |
|||
user: document.getElementById('formUser').value, |
|||
type: document.getElementById('formType').value, |
|||
contact: document.getElementById('formContact').value, |
|||
phone: document.getElementById('formPhone').value, |
|||
IOno: document.getElementById('IDno').value, |
|||
}; |
|||
try{ |
|||
const jsondata = await postJSON('/api/assets/addUpdateOwners', {data,currentMode}); |
|||
bsuccess = jsondata.bsuccess; |
|||
error = jsondata.error; |
|||
if(bsuccess){ |
|||
ownersData = jsondata.owner_list || []; |
|||
currentPage = 1; |
|||
document.querySelector('#ownerModal .btn-close').click(); |
|||
renderTable(); |
|||
}else { |
|||
alert("操作失败"+error) |
|||
} |
|||
} catch (error) { |
|||
console.error("操作失败:",error) |
|||
alert("操作失败:"+error) |
|||
} |
|||
} |
|||
|
|||
function renderTable() { |
|||
const tableBody = document.querySelector('#ownerTable tbody'); |
|||
//const tableBody = document.getElementById('tableBody'); |
|||
tableBody.innerHTML = ''; |
|||
const start = (currentPage - 1) * pageSize; |
|||
const pageData = ownersData.slice(start, start + pageSize); |
|||
|
|||
pageData.forEach((item, i) => { //IP,itype,uname,tellnum,tell_username,ID_num |
|||
const tr = document.createElement('tr'); |
|||
tr.innerHTML = ` |
|||
<td>${start + i + 1}</td> |
|||
<td>${item[2] || ''}</td> |
|||
<td>${item[1] || ''}</td> |
|||
<td>${item[4] || ''}</td> |
|||
<td>${item[3] || ''}</td> |
|||
<td>${item[8] || ''}</td> |
|||
<td> |
|||
<button class="btn btn-info btn-sm me-1" onclick="openModal('edit', ${start + i})">修改</button> |
|||
<!-- <button class="btn btn-info btn-sm me-1" onclick="showassets(${start + i})">查看资产</button> --> |
|||
<button class="btn btn-danger btn-sm" onclick="delowner(${start + i})">删除</button> |
|||
</td> |
|||
`; |
|||
tableBody.appendChild(tr); |
|||
}); |
|||
|
|||
for (let i = pageData.length; i < pageSize; i++) { |
|||
const tr = document.createElement('tr'); |
|||
tr.innerHTML = '<td colspan="7"> </td>'; |
|||
tableBody.appendChild(tr); |
|||
} |
|||
} |
|||
|
|||
async function fetchData() { |
|||
const ownerEl = document.getElementById("searchUser"); |
|||
const ownerTypeEl = document.getElementById("ownerType"); |
|||
const contactEl = document.getElementById("searchContact"); |
|||
const tellNumEl = document.getElementById("searchPhone"); |
|||
const owner = ownerEl.value; |
|||
const owner_type = ownerTypeEl.value; |
|||
const contact = contactEl.value; |
|||
const tellnum = tellNumEl.value; |
|||
try { |
|||
const data = await postJSON("/api/assets/getOwners",{owner,owner_type,contact,tellnum}) |
|||
ownersData = data.owner_list || []; |
|||
currentPage = 1; |
|||
renderTable(); |
|||
} catch (error) { |
|||
console.error("查询资产用户记录出错:", error); |
|||
alert("查询失败!"); |
|||
} |
|||
} |
|||
|
|||
async function showassets(index){ |
|||
} |
|||
|
|||
async function delowner(index){ |
|||
const data = ownersData[index]; |
|||
const id = data[0] |
|||
if (!confirm('确认删除?')) return; |
|||
try{ |
|||
const redata = await postJSON('/api/assets/delOwners', {id}); |
|||
bsuccess = redata.bsuccess; |
|||
error = redata.error; |
|||
if(bsuccess){ |
|||
alert("删除成功!"); |
|||
await fetchData(); //刷新数据 |
|||
} |
|||
else{ |
|||
alert("删除失败!",error) |
|||
} |
|||
} catch (error) { |
|||
console.error("删除失败!",error); |
|||
alert("删除失败!",error) |
|||
} |
|||
} |
|||
|
|||
window.onload = fetchData; |
|||
</script> |
|||
{% endblock %} |
@ -0,0 +1,108 @@ |
|||
{% extends 'base.html' %} |
|||
|
|||
{% block title %}ZFSAFE{% endblock %} |
|||
|
|||
<!-- 页面样式块 --> |
|||
{% block style %} |
|||
/* 查询条件区域:使用 row 分布,输入框占满所在列 */ |
|||
.search-section .form-control, |
|||
.search-section .form-select { |
|||
width: 100%; |
|||
} |
|||
/* 查询条件区域,每个条件统一高度且左右间隔均等 */ |
|||
.search-section .col { |
|||
padding: 0 5px; |
|||
} |
|||
|
|||
/* 表格样式:统一垂直居中 */ |
|||
.table thead th, .table tbody td { |
|||
vertical-align: middle; |
|||
text-align: center; |
|||
} |
|||
|
|||
/* 分页区域右对齐 */ |
|||
.pagination-section { |
|||
text-align: right; |
|||
padding-right: 15px; |
|||
} |
|||
|
|||
/* 固定行高,比如 45px,每页 10 行 */ |
|||
.fixed-row-height { |
|||
height: 45px; |
|||
overflow: hidden; |
|||
} |
|||
{% endblock %} |
|||
|
|||
<!-- 页面内容块 --> |
|||
{% block content %} |
|||
<div class="container-xxl mt-2"> |
|||
<!-- 查询区 --> |
|||
<div class="row mb-3"> |
|||
<div class="col-2 mb-2"><button class="btn btn-primary me-3" onclick="openModal('add')">导入</button></div> |
|||
<div class="col-10"></div> |
|||
<div class="col-3 mb-2"><input type="text" class="form-control" id="polltarget" placeholder="检测目标"></div> |
|||
<div class="col-3"><input type="text" class="form-control" id="owner" placeholder="所属用户"></div> |
|||
<div class="col-2"> |
|||
<select class="form-select" id="polling_period"> |
|||
<option value="">巡检周期</option> |
|||
<option value="1">每日</option> |
|||
<option value="2">每周</option> |
|||
<option value="3">每月</option> |
|||
</select> |
|||
</div> |
|||
<div class="col-2"> |
|||
<select class="form-select" id="risk_rank"> |
|||
<option value="">风险等级</option> |
|||
<option value="0">0</option> |
|||
<option value="1">1</option> |
|||
<option value="2">2</option> |
|||
<option value="3">3</option> |
|||
<option value="4">4</option> |
|||
<option value="5">5</option> |
|||
</select> |
|||
</div> |
|||
<div class="col-2 text-end"> |
|||
<button class="btn btn-primary" onclick="fetchData()">查询</button> |
|||
<button class="btn btn-primary" onclick="exportOwnerData()">导出</button> |
|||
</div> |
|||
</div> |
|||
<!-- 表格 --> |
|||
<div class="table-responsive"> |
|||
<table class="table table-bordered table-hover" id="pollingTable" style="width: 100%; table-layout: fixed;"> |
|||
<thead> |
|||
<tr> |
|||
<th style="width:5%;">序号</th> |
|||
<th style="width:20%;">检测目标</th> |
|||
<th style="width:20%;">所属用户</th> |
|||
<th style="width:10%;">检测周期</th> |
|||
<th style="width:15%;">最新检测时间</th> |
|||
<th style="width:10%;">风险等级</th> |
|||
<th style="width:20%;">操作</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<!-- JS 动态插入 10 行 --> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<!-- 分页 --> |
|||
<nav class="mt-2"> |
|||
<ul class="pagination justify-content-end" id="pollingPagination"> |
|||
<li class="page-item"><a class="page-link" href="#" id="pollingPrev">上一页</a></li> |
|||
<li class="page-item"><a class="page-link" href="#" id="pollingNext">下一页</a></li> |
|||
</ul> |
|||
</nav> |
|||
|
|||
</div> |
|||
|
|||
<!-- --------导入modal---------- --> |
|||
|
|||
<!-- --------所属用户modal---------- --> |
|||
|
|||
<!-- --------巡检策略modal---------- --> |
|||
|
|||
{% endblock %} |
|||
|
|||
<!-- 页面脚本块 --> |
|||
{% block script %} |
|||
{% endblock %} |
@ -0,0 +1,16 @@ |
|||
{% extends 'base.html' %} |
|||
|
|||
{% block title %}ZFSAFE{% endblock %} |
|||
|
|||
<!-- 页面样式块 --> |
|||
{% block style %} |
|||
{% endblock %} |
|||
|
|||
<!-- 页面内容块 --> |
|||
{% block content %} |
|||
<h3 style="text-align: center;padding: 10px"> 功能规划中,在二期实现。。。</h3> |
|||
{% endblock %} |
|||
|
|||
<!-- 页面脚本块 --> |
|||
{% block script %} |
|||
{% endblock %} |
Loading…
Reference in new issue