- 需求 v1.1 + 2026-09-29 需求方确认的 Q7~Q19 决议(含 BR-07/BR-09/6.7/6.9 矛盾表述修订) - 测试点 159 个、测试用例 164 条(P0 55/P1 81/P2 23/P3 5),需求→测试点→用例三级追溯覆盖率 100% - 质量裁决 PASS(首轮 PASS_WITH_FIX,13 项待确认确认后复裁决升级);评审原始 2 阻断项+9 建议项+3 优化项已全部修复 - 确认单:与 4 个历史需求均为并行关系、0 冲突、0 回写 - 附导出 v1/v2 版本快照、Playwright/Appium 脚本骨架、覆盖率审计脚本与指标、评审修复脚本
1333 lines
78 KiB
HTML
1333 lines
78 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title>物流公司管理 - 交互原型</title>
|
||
<style>
|
||
* { margin:0; padding:0; box-sizing:border-box; }
|
||
body { font-family:"Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",Arial,sans-serif; font-size:14px; color:#333; background:#f0f2f5; }
|
||
a { text-decoration:none; }
|
||
.app { display:flex; min-height:100vh; }
|
||
|
||
/* ===== 侧边栏 ===== */
|
||
.sidebar { width:200px; background:#263445; color:#bfcbd9; flex-shrink:0; min-height:100vh; overflow-y:auto; }
|
||
.logo { height:56px; display:flex; align-items:center; justify-content:center; color:#fff; font-size:16px; font-weight:bold; background:#1f2d3d; letter-spacing:1px; }
|
||
.menu { list-style:none; padding-bottom:40px; }
|
||
.menu > li { line-height:44px; }
|
||
.menu .mi { display:block; padding:0 20px; cursor:pointer; white-space:nowrap; }
|
||
.menu .mi:hover { background:#1f2d3d; }
|
||
.menu .mi .arrow { float:right; font-size:12px; color:#8a9bb0; }
|
||
.submenu { list-style:none; background:#1f2d3d; display:none; }
|
||
.submenu.open { display:block; }
|
||
.submenu li { padding-left:34px; line-height:40px; cursor:pointer; white-space:nowrap; }
|
||
.submenu li:hover { color:#fff; }
|
||
.submenu li.active { color:#409eff; }
|
||
.submenu .sub2 { list-style:none; background:#1a2433; display:none; }
|
||
.submenu .sub2.open { display:block; }
|
||
.submenu .sub2 li { padding-left:50px; line-height:36px; font-size:13px; }
|
||
|
||
/* ===== 主区域 ===== */
|
||
.main { flex:1; display:flex; flex-direction:column; min-width:0; }
|
||
.topbar { height:50px; background:#fff; display:flex; align-items:center; padding:0 16px; border-bottom:1px solid #e6e6e6; box-shadow:0 1px 4px rgba(0,21,41,.08); position:relative; z-index:5; }
|
||
.topbar .fold { font-size:18px; cursor:pointer; color:#5a5e66; margin-right:12px; }
|
||
.crumb { color:#97a8be; font-size:13px; }
|
||
.crumb b { color:#333; font-weight:normal; }
|
||
.crumb .sep { margin:0 6px; }
|
||
.topbar .right { margin-left:auto; display:flex; align-items:center; gap:16px; color:#5a5e66; }
|
||
.avatar { width:30px; height:30px; border-radius:50%; background:#409eff; color:#fff; display:inline-flex; align-items:center; justify-content:center; font-size:12px; }
|
||
.tabsbar { background:#fff; padding:6px 12px 0; border-bottom:1px solid #e6e6e6; display:flex; gap:6px; flex-wrap:wrap; }
|
||
.chip { background:#fff; border:1px solid #d8dce5; color:#495060; padding:4px 10px; font-size:12px; border-radius:2px; cursor:pointer; margin-bottom:6px; display:inline-flex; align-items:center; gap:6px; }
|
||
.chip .x { color:#9aa2b1; font-size:13px; }
|
||
.chip.active { background:#67c23a; border-color:#67c23a; color:#fff; }
|
||
.chip.active .x { color:#e6f7dd; }
|
||
.demo-strip { background:#fdf6ec; color:#b88230; font-size:12px; padding:5px 16px; border-bottom:1px solid #faecd8; }
|
||
.content { flex:1; padding:14px; }
|
||
|
||
/* ===== 卡片 / 页面 ===== */
|
||
.page { display:none; }
|
||
.page.active { display:block; }
|
||
.card { background:#fff; border-radius:4px; padding:18px; box-shadow:0 1px 2px rgba(0,0,0,.03); margin-bottom:14px; }
|
||
.page-title { font-size:15px; font-weight:bold; color:#303133; margin-bottom:14px; padding-left:8px; border-left:3px solid #409eff; }
|
||
.section-title { font-size:14px; color:#303133; font-weight:bold; margin:6px 0 12px; }
|
||
|
||
/* ===== 按钮 ===== */
|
||
.btn { display:inline-block; padding:8px 16px; font-size:13px; border-radius:3px; border:1px solid #dcdfe6; background:#fff; color:#606266; cursor:pointer; outline:none; }
|
||
.btn:hover { opacity:.9; }
|
||
.btn.primary { background:#409eff; border-color:#409eff; color:#fff; }
|
||
.btn.success { background:#67c23a; border-color:#67c23a; color:#fff; }
|
||
.btn.danger { background:#f56c6c; border-color:#f56c6c; color:#fff; }
|
||
.btn.mini { padding:4px 10px; font-size:12px; }
|
||
.btn:disabled { opacity:.5; cursor:not-allowed; }
|
||
.link { color:#409eff; cursor:pointer; margin-right:10px; font-size:13px; }
|
||
.link.danger { color:#f56c6c; }
|
||
.btn-row { margin-top:18px; padding-top:14px; border-top:1px solid #ebeef5; }
|
||
.btn-row .btn { margin-right:10px; }
|
||
|
||
/* ===== 表单 ===== */
|
||
.form-grid { display:grid; grid-template-columns:1fr 1fr; gap:2px 40px; max-width:1150px; }
|
||
.form-item { display:flex; align-items:flex-start; padding:10px 0; }
|
||
.form-item.full { grid-column:1 / -1; }
|
||
.form-item label { width:150px; text-align:right; padding-right:10px; line-height:32px; color:#606266; flex-shrink:0; font-size:13px; }
|
||
.req::before { content:"*"; color:#f56c6c; margin-right:3px; }
|
||
.form-item .ctrl { flex:1; min-width:0; }
|
||
.input, .select, .textarea { width:100%; height:32px; border:1px solid #dcdfe6; border-radius:3px; padding:0 10px; font-size:13px; color:#606266; outline:none; background:#fff; }
|
||
.input:focus, .select:focus, .textarea:focus { border-color:#409eff; }
|
||
.input.err, .select.err { border-color:#f56c6c; }
|
||
.textarea { height:64px; padding:6px 10px; resize:vertical; }
|
||
.tip { color:#909399; font-size:12px; line-height:32px; margin-left:8px; white-space:nowrap; }
|
||
.err-msg { color:#f56c6c; font-size:12px; line-height:1.4; margin-top:2px; display:none; }
|
||
.form-item.has-err .err-msg { display:block; }
|
||
|
||
/* ===== 上传证件框 ===== */
|
||
.upload-row { display:flex; gap:22px; flex-wrap:wrap; margin-bottom:8px; }
|
||
.upload-box { width:150px; cursor:pointer; }
|
||
.up-frame { width:150px; height:150px; border:1px dashed #c0c4cc; border-radius:4px; display:flex; align-items:center; justify-content:center; color:#c0c4cc; position:relative; background:#fafbfc; }
|
||
.up-frame .plus { font-size:44px; font-weight:200; }
|
||
.up-frame.done { border-style:solid; border-color:#67c23a; background:#f0f9eb; color:#67c23a; flex-direction:column; font-size:12px; gap:6px; }
|
||
.up-frame.done .plus { display:none; }
|
||
.up-label { text-align:center; margin-top:8px; color:#606266; font-size:13px; }
|
||
.up-label.req::before { content:"*"; color:#f56c6c; margin-right:3px; }
|
||
|
||
/* ===== 筛选区 / 表格 ===== */
|
||
.filter-bar { display:flex; flex-wrap:wrap; gap:10px 14px; align-items:center; margin-bottom:14px; }
|
||
.filter-bar .fi { display:flex; align-items:center; gap:6px; }
|
||
.filter-bar .input, .filter-bar .select { width:180px; }
|
||
.filter-bar .input.date { width:145px; }
|
||
.btn-bar { display:flex; gap:10px; margin-bottom:14px; }
|
||
.table-wrap { overflow-x:auto; }
|
||
table.tbl { width:100%; border-collapse:collapse; font-size:13px; }
|
||
.tbl th, .tbl td { border-bottom:1px solid #ebeef5; padding:10px 8px; text-align:left; color:#606266; white-space:nowrap; }
|
||
.tbl th { background:#f8f8f9; color:#515a6e; font-weight:bold; }
|
||
.tbl tbody tr:nth-child(even) { background:#fafbfc; }
|
||
.tbl tbody tr:hover { background:#f5f7fa; }
|
||
.tbl .name-link { color:#409eff; cursor:pointer; }
|
||
.tbl td .mini-input { width:110px; height:28px; border:1px solid #dcdfe6; border-radius:3px; padding:0 8px; font-size:12px; color:#909399; }
|
||
.tag { display:inline-block; padding:2px 8px; font-size:12px; border-radius:3px; line-height:18px; }
|
||
.tag.green { background:#f0f9eb; color:#67c23a; border:1px solid #e1f3d8; }
|
||
.tag.orange { background:#fdf6ec; color:#e6a23c; border:1px solid #faecd8; }
|
||
.tag.gray { background:#f4f4f5; color:#909399; border:1px solid #e9e9eb; }
|
||
.tag.blue { background:#ecf5ff; color:#409eff; border:1px solid #d9ecff; }
|
||
.tag.red { background:#fef0f0; color:#f56c6c; border:1px solid #fde2e2; }
|
||
.tbl tr.frow th { padding:6px 4px; }
|
||
.frow .input, .frow .select { height:28px; font-size:12px; color:#909399; }
|
||
.warn-txt { color:#e6a23c; font-size:12px; }
|
||
|
||
/* 开关 */
|
||
.switch { display:inline-block; width:36px; height:18px; border-radius:9px; background:#dcdfe6; position:relative; cursor:pointer; vertical-align:middle; transition:background .2s; }
|
||
.switch::after { content:""; position:absolute; top:2px; left:2px; width:14px; height:14px; border-radius:50%; background:#fff; transition:left .2s; }
|
||
.switch.on { background:#409eff; }
|
||
.switch.on::after { left:20px; }
|
||
|
||
/* 分页 */
|
||
.pagination { display:flex; align-items:center; justify-content:flex-end; gap:8px; margin-top:14px; font-size:13px; color:#606266; }
|
||
.pg-btn { min-width:28px; height:28px; border:1px solid #dcdfe6; border-radius:3px; background:#fff; color:#606266; cursor:pointer; font-size:12px; }
|
||
.pg-btn.cur { background:#409eff; border-color:#409eff; color:#fff; }
|
||
.pagination select { height:28px; border:1px solid #dcdfe6; border-radius:3px; }
|
||
|
||
/* Tab */
|
||
.tab-nav { display:flex; gap:2px; background:#fff; padding:0 18px; border-bottom:1px solid #e4e7ed; border-radius:4px 4px 0 0; }
|
||
.tab-item { padding:12px 20px; cursor:pointer; color:#303133; font-size:14px; border-bottom:2px solid transparent; }
|
||
.tab-item:hover { color:#409eff; }
|
||
.tab-item.active { color:#409eff; border-bottom-color:#409eff; font-weight:bold; }
|
||
.tab-panel { display:none; }
|
||
.tab-panel.active { display:block; }
|
||
|
||
/* 弹窗 */
|
||
.mask { display:none; position:fixed; inset:0; background:rgba(0,0,0,.5); z-index:100; align-items:flex-start; justify-content:center; }
|
||
.mask.open { display:flex; }
|
||
.dialog { background:#fff; border-radius:4px; min-width:560px; max-width:900px; margin-top:80px; box-shadow:0 4px 20px rgba(0,0,0,.2); }
|
||
.dialog .dlg-head { display:flex; align-items:center; padding:14px 18px; border-bottom:1px solid #ebeef5; font-weight:bold; color:#303133; }
|
||
.dialog .dlg-head .close { margin-left:auto; cursor:pointer; color:#909399; font-size:18px; line-height:1; }
|
||
.dialog .dlg-body { padding:22px 26px 6px; max-height:60vh; overflow-y:auto; }
|
||
.dialog .dlg-foot { padding:12px 18px 16px; text-align:right; }
|
||
.dialog .dlg-foot .btn { margin-left:10px; min-width:70px; }
|
||
.dlg-form .form-item label { width:110px; }
|
||
|
||
/* 已选公司 / 上传按钮 */
|
||
.pick-summary { display:flex; align-items:center; gap:10px; margin:12px 0 6px; }
|
||
.pick-summary .count { color:#409eff; font-weight:bold; }
|
||
.upload-btn { display:inline-block; padding:7px 14px; background:#409eff; color:#fff; border-radius:3px; font-size:13px; cursor:pointer; }
|
||
.file-name { margin-left:10px; color:#67c23a; font-size:13px; }
|
||
.hint { color:#909399; font-size:12px; margin-top:8px; }
|
||
|
||
/* Toast */
|
||
#toast-wrap { position:fixed; top:60px; left:50%; transform:translateX(-50%); z-index:200; display:flex; flex-direction:column; gap:10px; align-items:center; }
|
||
.toast { padding:9px 18px; border-radius:4px; color:#fff; font-size:13px; box-shadow:0 2px 12px rgba(0,0,0,.15); animation:fadein .2s; }
|
||
.toast.success { background:#67c23a; }
|
||
.toast.error { background:#f56c6c; }
|
||
.toast.warning { background:#e6a23c; }
|
||
@keyframes fadein { from { opacity:0; transform:translateY(-8px);} to { opacity:1; transform:none;} }
|
||
|
||
.empty { text-align:center; color:#909399; padding:30px 0; font-size:13px; }
|
||
.home-cards { display:flex; gap:14px; flex-wrap:wrap; }
|
||
.stat-card { background:#fff; border-radius:4px; padding:18px 22px; min-width:200px; box-shadow:0 1px 2px rgba(0,0,0,.03); }
|
||
.stat-card .num { font-size:24px; font-weight:bold; color:#303133; margin-top:6px; }
|
||
.stat-card .lbl { color:#909399; font-size:13px; }
|
||
.stat-card .num.blue { color:#409eff; }
|
||
.stat-card .num.green { color:#67c23a; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<!-- ===== 侧边栏 ===== -->
|
||
<aside class="sidebar">
|
||
<div class="logo">Ⓦ 运八网络货运平台系统</div>
|
||
<ul class="menu">
|
||
<li><span class="mi" onclick="go('home')">✈ 首页</span></li>
|
||
<li>
|
||
<span class="mi" onclick="toggleMenu('m-basic',this)">👤 基础信息 <span class="arrow">⌃</span></span>
|
||
<ul class="submenu open" id="m-basic">
|
||
<li onclick="toggleMenu('m-platform',this)">平台管理 ⌃
|
||
<ul class="sub2" id="m-platform"><li onclick="todo()">账号管理</li><li onclick="todo()">角色管理</li></ul>
|
||
</li>
|
||
<li onclick="toggleMenu('m-owner',this)">货主管理 ⌃
|
||
<ul class="sub2" id="m-owner">
|
||
<li onclick="todo()">个体货主</li><li onclick="todo()">货主信息</li><li onclick="todo()">企业货主</li><li onclick="todo()">企业信息</li><li onclick="todo()">分账记录</li>
|
||
</ul>
|
||
</li>
|
||
<li onclick="toggleMenu('m-carrier',this)">承运管理 ⌃
|
||
<ul class="sub2 open" id="m-carrier">
|
||
<li onclick="todo()">车队管理</li><li onclick="todo()">司机管理</li><li onclick="todo()">平台车辆</li>
|
||
<li class="active" onclick="go('list')">物流公司管理 <span style="color:#67c23a;font-size:11px;">NEW</span></li>
|
||
</ul>
|
||
</li>
|
||
<li onclick="toggleMenu('m-veh',this)">车辆服务 ⌃
|
||
<ul class="sub2" id="m-veh"><li onclick="todo()">车辆保险</li><li onclick="todo()">车辆维修</li></ul>
|
||
</li>
|
||
<li onclick="todo()">数字字典</li>
|
||
<li onclick="todo()">银行管理</li>
|
||
<li onclick="todo()">审核管理</li>
|
||
<li onclick="todo()">商户中心</li>
|
||
<li onclick="todo()">账户管理</li>
|
||
<li onclick="todo()">彩虹糖</li>
|
||
</ul>
|
||
</li>
|
||
<li>
|
||
<span class="mi" onclick="toggleMenu('m-tms',this)">🚚 整车运输 <span class="arrow">⌃</span></span>
|
||
<ul class="submenu" id="m-tms">
|
||
<li onclick="toggleMenu('m-goods',this)">货源管理 ⌃
|
||
<ul class="sub2" id="m-goods">
|
||
<li onclick="go('source')">发布货源</li>
|
||
<li onclick="go('goodslist')">货源管理 <span style="color:#67c23a;font-size:11px;">NEW</span></li>
|
||
<li onclick="todo()">货源大厅</li>
|
||
<li onclick="todo()">项目货源</li>
|
||
<li onclick="todo()">货源审核</li>
|
||
</ul>
|
||
</li>
|
||
<li onclick="toggleMenu('m-trans',this)">运输管理 ⌃
|
||
<ul class="sub2" id="m-trans">
|
||
<li onclick="todo()">运单跟踪</li>
|
||
<li onclick="go('waybill')">运单管理 <span style="color:#67c23a;font-size:11px;">NEW</span></li>
|
||
<li onclick="todo()">运单轨迹</li>
|
||
</ul>
|
||
</li>
|
||
<li onclick="toggleMenu('m-tms-settle',this)">承运结算 ⌃
|
||
<ul class="sub2" id="m-tms-settle">
|
||
<li onclick="go('settle')">运费核算 <span style="color:#67c23a;font-size:11px;">NEW</span></li>
|
||
<li onclick="gotoPay('owner')">货主打款</li>
|
||
<li onclick="gotoPay('finance')">财务打款</li>
|
||
<li onclick="todo()">打款记录</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</aside>
|
||
|
||
<!-- ===== 主区域 ===== -->
|
||
<div class="main">
|
||
<header class="topbar">
|
||
<span class="fold">☰</span>
|
||
<span class="crumb" id="crumb"></span>
|
||
<div class="right">
|
||
<span title="搜索" style="cursor:pointer;">🔍</span>
|
||
<span title="全屏" style="cursor:pointer;">⛶</span>
|
||
<span class="avatar">管</span><span style="font-size:13px;">超级管理员</span>
|
||
</div>
|
||
</header>
|
||
<nav class="tabsbar" id="tabsbar">
|
||
<span class="chip" data-page="home" onclick="go('home')">首页 <span class="x">×</span></span>
|
||
<span class="chip" data-page="list" onclick="go('list')">物流公司列表 <span class="x">×</span></span>
|
||
<span class="chip" data-page="add" onclick="go('add')">新增物流公司 <span class="x">×</span></span>
|
||
<span class="chip" data-page="edit" onclick="go('edit')">编辑物流公司 <span class="x">×</span></span>
|
||
<span class="chip" data-page="source" onclick="go('source')">发布货源 <span class="x">×</span></span>
|
||
<span class="chip" data-page="settle" onclick="go('settle')">运费核算 <span class="x">×</span></span>
|
||
<span class="chip" data-page="pay" onclick="go('pay')">打款列表 <span class="x">×</span></span>
|
||
<span class="chip" data-page="goodslist" onclick="go('goodslist')">货源列表 <span class="x">×</span></span>
|
||
<span class="chip" data-page="waybill" onclick="go('waybill')">运单管理 <span class="x">×</span></span>
|
||
</nav>
|
||
<div class="demo-strip">交互原型:数据为演示数据、不落库;物流公司仅作运单记录、不影响任何流程;核算=把核算数据填充到运单表(无独立核算单);筛选样式参照运八真实页面(表头筛选行)。</div>
|
||
|
||
<main class="content">
|
||
|
||
<!-- ===== 首页 ===== -->
|
||
<section class="page" id="page-home">
|
||
<div class="home-cards">
|
||
<div class="stat-card"><div class="lbl">物流公司总数</div><div class="num" id="stat-total">0</div></div>
|
||
<div class="stat-card"><div class="lbl">启用中</div><div class="num green" id="stat-active">0</div></div>
|
||
<div class="stat-card"><div class="lbl">生效合同(份)</div><div class="num blue" id="stat-contracts">0</div></div>
|
||
</div>
|
||
<div class="card" style="margin-top:14px;">
|
||
<div class="page-title">快捷入口</div>
|
||
<div class="btn-bar" style="margin-bottom:0;">
|
||
<button class="btn primary" onclick="go('add')">新增物流公司</button>
|
||
<button class="btn" onclick="go('list')">物流公司列表</button>
|
||
<button class="btn success" onclick="go('settle')">运费核算(运单核算演示)</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 物流公司列表 ===== -->
|
||
<section class="page" id="page-list">
|
||
<div class="card">
|
||
<div class="btn-bar">
|
||
<button class="btn primary" onclick="renderCompanyTable()">查询</button>
|
||
<button class="btn" onclick="resetQuery()">重置</button>
|
||
<button class="btn success" onclick="go('add')">新增</button>
|
||
<button class="btn" onclick="toast('演示:导出 Excel 成功(上限 20000 条)','success')">导出</button>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr class="frow"><th></th><th><input class="input" id="q-name" placeholder="请输入公司名称"></th><th></th><th><input class="input" id="q-uscc" placeholder="请输入信用代码"></th><th></th><th></th><th></th><th></th><th><select class="select" id="q-status"><option value="">请选择状态</option><option value="1">启用</option><option value="0">停用</option></select></th><th></th><th><input type="date" class="input date" id="q-start" style="width:105px;"><input type="date" class="input date" id="q-end" style="width:105px;"></th><th></th></tr><tr>
|
||
<th style="width:34px;"><input type="checkbox" id="ck-all-company" onchange="toggleAllCompany(this)"></th>
|
||
<th>公司名称</th><th>简称码</th><th>统一社会信用代码</th><th>法人代表名称</th><th>联系人</th><th>联系方式</th>
|
||
<th>生效合同数</th><th>状态</th><th>创建人</th><th>创建时间</th><th style="width:110px;">操作</th>
|
||
</tr></thead>
|
||
<tbody id="company-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination">
|
||
<span id="pg-info"></span>
|
||
<button class="pg-btn">«</button><button class="pg-btn">‹</button>
|
||
<button class="pg-btn cur">1</button><button class="pg-btn">›</button><button class="pg-btn">»</button>
|
||
<select><option>20条/页</option><option>50条/页</option></select>
|
||
<span>前往</span><input class="input" style="width:44px;height:28px;" value="1"><span>页</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 新增物流公司 ===== -->
|
||
<section class="page" id="page-add">
|
||
<div class="card">
|
||
<div class="page-title">新增物流公司</div>
|
||
<div class="section-title">上传证件</div>
|
||
<div class="upload-row">
|
||
<div class="upload-box" data-required="1" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label req">企业营业执照</div>
|
||
</div>
|
||
<div class="upload-box" data-required="1" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label req">法人身份证人像面照片</div>
|
||
</div>
|
||
<div class="upload-box" data-required="1" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label req">法人身份证国徽面照片</div>
|
||
</div>
|
||
<div class="upload-box" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label">道路运输经营许可证</div>
|
||
</div>
|
||
</div>
|
||
<div class="hint" style="margin-bottom:10px;">支持 jpg/jpeg/png/pdf;点击证件框模拟上传 / 取消上传(附件暂不设大小上限)。</div>
|
||
<div class="form-grid" id="add-form"></div>
|
||
<div class="btn-row">
|
||
<button class="btn primary" onclick="saveCompany('add')">保 存</button>
|
||
<button class="btn" onclick="go('list')">取 消</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 编辑物流公司 ===== -->
|
||
<section class="page" id="page-edit">
|
||
<div class="card" style="padding:0;">
|
||
<div class="tab-nav">
|
||
<div class="tab-item active" data-tab="profile" onclick="switchTab('profile')">企业资料</div>
|
||
<div class="tab-item" data-tab="contract" onclick="switchTab('contract')">合同管理</div>
|
||
</div>
|
||
<div style="padding:18px;">
|
||
<!-- 企业资料 -->
|
||
<div class="tab-panel active" id="tab-profile">
|
||
<div class="section-title">上传证件</div>
|
||
<div class="upload-row">
|
||
<div class="upload-box" data-required="1" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label req">企业营业执照</div>
|
||
</div>
|
||
<div class="upload-box" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label">法人身份证人像面照片</div>
|
||
</div>
|
||
<div class="upload-box" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label">法人身份证国徽面照片</div>
|
||
</div>
|
||
<div class="upload-box" onclick="toggleUpload(this)">
|
||
<div class="up-frame"><span class="plus">+</span></div>
|
||
<div class="up-label">道路运输经营许可证</div>
|
||
</div>
|
||
</div>
|
||
<div class="hint" style="margin-bottom:10px;">点击证件框模拟上传 / 取消上传(附件暂不设大小上限)。</div>
|
||
<div class="form-grid" id="edit-form"></div>
|
||
<div class="btn-row">
|
||
<button class="btn primary" onclick="saveCompany('edit')">保 存</button>
|
||
<button class="btn" onclick="go('list')">返 回</button>
|
||
</div>
|
||
</div>
|
||
<!-- 合同管理 -->
|
||
<div class="tab-panel" id="tab-contract">
|
||
<div class="btn-bar">
|
||
<button class="btn primary" onclick="renderContractTable()">查询</button>
|
||
<button class="btn success" onclick="openContractDialog('add')">新增</button>
|
||
<button class="btn" onclick="toast('演示:合同列表导出成功','success')">导出</button>
|
||
</div>
|
||
<div class="filter-bar">
|
||
<input class="mini-input" id="cf-no" placeholder="请输入合同编号">
|
||
<select class="select" id="cf-tax" style="width:220px;"><option value="">请选择税源地</option></select>
|
||
<input class="mini-input" id="cf-creator" placeholder="请输入创建人" style="width:130px;">
|
||
<span>签订时间</span><input type="date" class="mini-input" id="cf-sign1" style="width:135px;"><span>-</span><input type="date" class="mini-input" id="cf-sign2" style="width:135px;">
|
||
<span>创建时间</span><input type="date" class="mini-input" id="cf-create1" style="width:135px;"><span>-</span><input type="date" class="mini-input" id="cf-create2" style="width:135px;">
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr>
|
||
<th style="width:34px;"><input type="checkbox" onchange="toggleAllContract(this)"></th>
|
||
<th>合同编号</th><th>税源地</th><th>合同状态</th><th>创建人</th><th>签订时间</th><th>创建时间</th><th>生效时间</th><th>过期时间</th><th style="width:130px;">操作</th>
|
||
</tr></thead>
|
||
<tbody id="contract-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination">
|
||
<span id="cpg-info"></span>
|
||
<button class="pg-btn">«</button><button class="pg-btn">‹</button>
|
||
<button class="pg-btn cur">1</button><button class="pg-btn">›</button><button class="pg-btn">»</button>
|
||
<select><option>20条/页</option><option>50条/页</option></select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 运费核算(运单核算) ===== -->
|
||
<section class="page" id="page-settle">
|
||
<div class="card">
|
||
<div class="page-title">运费核算(整车运输 → 承运结算)</div>
|
||
<div class="hint" style="margin-bottom:10px;">核算即把核算数据填充到运单表,无独立核算单;点击行内"修改核算"填写本期新增字段。</div>
|
||
<div class="btn-bar">
|
||
<button class="btn primary" onclick="toast('演示:查询条件已应用','success')">查询</button>
|
||
<button class="btn" onclick="toast('演示:批量核算沿用现有逻辑(Q7)','warning')">批量核算</button>
|
||
<button class="btn" onclick="toast('演示:导出成功','success')">导出</button>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr class="frow"><th><input class="input" id="af-no" placeholder="请输入运输单号"></th><th></th><th></th><th></th><th></th><th><select class="select" id="af-status" onchange="renderAccountList()"><option value="">请选择核算状态</option><option>未核算</option><option>已核算</option></select></th><th></th><th></th></tr><tr>
|
||
<th>运输单号</th><th>货源单号</th><th>货物名称</th><th>承运司机</th><th>物流公司</th><th>核算状态</th><th>客户收货地址</th><th style="width:100px;">操作</th>
|
||
</tr></thead>
|
||
<tbody id="acc-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination"><span id="acc-pg"></span><button class="pg-btn">«</button><button class="pg-btn cur">1</button><button class="pg-btn">»</button><select><option>20条/页</option></select></div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 发布货源 ===== -->
|
||
<section class="page" id="page-source">
|
||
<div class="card">
|
||
<div class="page-title">发布货源(整车运输 → 货源管理)</div>
|
||
<div class="form-grid" style="max-width:900px;">
|
||
<div class="form-item"><label class="req">货源单编号</label><div class="ctrl"><input class="input" value="HY20260928-001" readonly style="background:#f5f7fa;"></div></div>
|
||
<div class="form-item"><label class="req">货物名称</label><div class="ctrl"><input class="input" id="src-goods" placeholder="请输入货物名称"></div></div>
|
||
<div class="form-item"><label class="req">起始地</label><div class="ctrl"><input class="input" id="src-from" placeholder="请输入起始地(原型模拟)"></div></div>
|
||
<div class="form-item"><label class="req">目的地</label><div class="ctrl"><input class="input" id="src-to" placeholder="请输入目的地(原型模拟)"></div></div>
|
||
<div class="form-item full">
|
||
<label>物流公司(选填)</label>
|
||
<div class="ctrl">
|
||
<button class="btn primary" onclick="openPickDialog('source')">选择物流公司</button>
|
||
<span class="tip">单选;仅作运单记录,不影响货源及运单流程</span>
|
||
<table class="tbl" style="max-width:760px;margin-top:10px;">
|
||
<thead><tr><th>公司名称</th><th>统一社会信用代码</th><th>联系人</th><th style="width:80px;">操作</th></tr></thead>
|
||
<tbody id="src-picked-tbody"><tr><td colspan="4" class="empty">未选择物流公司(选填)</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="form-item full"><label>备注</label><div class="ctrl"><textarea class="textarea" placeholder="选填"></textarea></div></div>
|
||
</div>
|
||
<div class="btn-row">
|
||
<button class="btn primary" onclick="saveSource()">保存货源</button>
|
||
<button class="btn" onclick="toast('演示:返回货源列表','success')">取 消</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 打款列表(货主打款 / 财务打款) ===== -->
|
||
<section class="page" id="page-pay">
|
||
<div class="card">
|
||
<div class="page-title">打款列表(整车运输 → 承运结算)</div>
|
||
<div class="tab-nav" style="padding:0;margin-bottom:12px;">
|
||
<div class="tab-item active" data-paytab="owner" onclick="switchPayTab('owner')">货主打款</div>
|
||
<div class="tab-item" data-paytab="finance" onclick="switchPayTab('finance')">财务打款</div>
|
||
</div>
|
||
<div class="btn-bar">
|
||
<button class="btn primary" onclick="renderPaymentTable()">查询</button>
|
||
<button class="btn" onclick="resetPayQuery()">重置</button>
|
||
<button class="btn" onclick="toast('演示:导出成功','success')">导出</button>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr class="frow">
|
||
<th><input class="input" id="pf-waybill" placeholder="请输入运输单号"></th>
|
||
<th></th>
|
||
<th><select class="select" id="pf-company" onchange="renderPaymentTable()"></select></th>
|
||
<th></th><th></th>
|
||
<th><select class="select" id="pf-status" onchange="renderPaymentTable()"><option value="">请选择状态</option><option>待打款</option><option>已打款</option><option>已驳回</option></select></th>
|
||
<th></th>
|
||
</tr><tr>
|
||
<th>运输单号</th><th>货源单号</th><th>物流公司</th><th>承运司机</th><th>打款金额(元)</th><th>打款状态</th><th>打款时间</th>
|
||
</tr></thead>
|
||
<tbody id="pay-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination"><span id="pay-pg"></span><button class="pg-btn">«</button><button class="pg-btn cur">1</button><button class="pg-btn">»</button><select><option>20条/页</option></select></div>
|
||
<div class="hint">货主打款、财务打款两个页面均含"物流公司"筛选与列(FR-09);仅作查询,不影响打款流程。</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 货源列表 ===== -->
|
||
<section class="page" id="page-goodslist">
|
||
<div class="card">
|
||
<div class="page-title">货源列表(整车运输 → 货源管理)</div>
|
||
<div class="btn-bar">
|
||
<button class="btn primary" onclick="renderGoodsTable()">查询</button>
|
||
<button class="btn" onclick="resetGoodsQuery()">重置</button>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr class="frow"><th><input class="input" id="gf-no" placeholder="请输入货源单号"></th><th><input class="input" id="gf-goods" placeholder="请输入货物名称"></th><th colspan="6"></th></tr><tr>
|
||
<th>货源单号</th><th>货物名称</th><th>起始地</th><th>目的地</th><th>物流公司</th><th>发布人</th><th>创建时间</th><th style="width:80px;">操作</th>
|
||
</tr></thead>
|
||
<tbody id="goods-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination"><span id="goods-pg"></span><button class="pg-btn">«</button><button class="pg-btn cur">1</button><button class="pg-btn">»</button><select><option>20条/页</option></select></div>
|
||
<div class="hint">"物流公司"列为货源上记录的快照信息(FR-10),只读展示、不影响任何流程。</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ===== 运单管理 ===== -->
|
||
<section class="page" id="page-waybill">
|
||
<div class="card">
|
||
<div class="page-title">运单管理(整车运输 → 运输管理)</div>
|
||
<div class="btn-bar">
|
||
<button class="btn primary" onclick="renderWaybillTable()">查询</button>
|
||
<button class="btn" onclick="resetWaybillQuery()">重置</button>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr class="frow"><th><input class="input" id="wf-no" placeholder="请输入运单号"></th><th></th><th><input class="input" id="wf-goods" placeholder="请输入货物名称"></th><th colspan="4"></th></tr><tr>
|
||
<th>运单号</th><th>货源单号</th><th>货物名称</th><th>承运司机</th><th>物流公司</th><th>运单状态</th><th>运费金额(元)</th>
|
||
</tr></thead>
|
||
<tbody id="waybill-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination"><span id="waybill-pg"></span><button class="pg-btn">«</button><button class="pg-btn cur">1</button><button class="pg-btn">»</button><select><option>20条/页</option></select></div>
|
||
<div class="hint">"物流公司"列随关联货源带入(FR-10),快照展示、只读,不影响任何流程。</div>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 新增/编辑合同弹窗 ===== -->
|
||
<div class="mask" id="mask-contract">
|
||
<div class="dialog" style="width:600px;">
|
||
<div class="dlg-head"><span id="contract-dlg-title">新增</span><span class="close" onclick="closeDialog('mask-contract')">×</span></div>
|
||
<div class="dlg-body dlg-form">
|
||
<div class="form-item"><label class="req">合同编号</label><div class="ctrl"><input class="input" id="ct-no" placeholder="请输入合同编号"><div class="err-msg" id="err-ct-no"></div></div></div>
|
||
<div class="form-item"><label class="req">签订时间</label><div class="ctrl"><input type="date" class="input" id="ct-sign"></div></div>
|
||
<div class="form-item"><label class="req">生效时间</label><div class="ctrl"><input type="date" class="input" id="ct-eff"></div></div>
|
||
<div class="form-item"><label class="req">过期时间</label><div class="ctrl"><input type="date" class="input" id="ct-exp"><div class="err-msg" id="err-ct-date"></div></div></div>
|
||
<div class="form-item"><label class="req">所属税源地</label><div class="ctrl"><select class="select" id="ct-tax"><option value="">请选择</option></select></div></div>
|
||
<div class="form-item"><label class="req">附件</label><div class="ctrl">
|
||
<span class="upload-btn" onclick="mockUploadFile()">点击上传</span>
|
||
<span class="file-name" id="ct-file-name"></span>
|
||
<div class="hint">只能上传pdf/doc/docx/jpg/jpeg/bmp/png文件</div>
|
||
</div></div>
|
||
</div>
|
||
<div class="dlg-foot">
|
||
<button class="btn" onclick="closeDialog('mask-contract')">取 消</button>
|
||
<button class="btn primary" onclick="saveContract()">确 定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 查看合同弹窗 ===== -->
|
||
<div class="mask" id="mask-view">
|
||
<div class="dialog" style="width:520px;">
|
||
<div class="dlg-head">合同详情<span class="close" onclick="closeDialog('mask-view')">×</span></div>
|
||
<div class="dlg-body" id="view-body"></div>
|
||
<div class="dlg-foot"><button class="btn" onclick="closeDialog('mask-view')">关 闭</button></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 修改核算弹窗(在运单上核算) ===== -->
|
||
<div class="mask" id="mask-account">
|
||
<div class="dialog" style="width:680px;">
|
||
<div class="dlg-head">修改核算<span class="close" onclick="closeDialog('mask-account')">×</span></div>
|
||
<div class="dlg-body">
|
||
<div class="section-title">运单信息(只读)</div>
|
||
<div class="form-grid" style="max-width:640px;">
|
||
<div class="form-item"><label>运单号</label><div class="ctrl"><input class="input" id="acc-no" readonly style="background:#f5f7fa;"></div></div>
|
||
<div class="form-item"><label>货物名称</label><div class="ctrl"><input class="input" id="acc-goods" readonly style="background:#f5f7fa;"></div></div>
|
||
<div class="form-item full"><label>承运司机</label><div class="ctrl"><input class="input" id="acc-driver" readonly style="background:#f5f7fa;"></div></div>
|
||
</div>
|
||
<div class="section-title" style="margin-top:10px;">核算字段(本期新增,填充到运单表)</div>
|
||
<div class="form-grid" style="max-width:640px;">
|
||
<div class="form-item full">
|
||
<label>物流公司(选填)</label>
|
||
<div class="ctrl">
|
||
<button class="btn primary" onclick="openPickDialog('account')">选择物流公司</button>
|
||
<span class="tip">单选;默认带入货源所选公司;无生效合同也可勾选</span>
|
||
<table class="tbl" style="max-width:520px;margin-top:10px;">
|
||
<thead><tr><th>公司名称</th><th>统一社会信用代码</th><th style="width:80px;">操作</th></tr></thead>
|
||
<tbody id="acc-picked-tbody"><tr><td colspan="3" class="empty">未选择物流公司(无外协公司)</td></tr></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
<div class="form-item full"><label>客户收货地址</label><div class="ctrl"><input class="input" id="acc-addr" placeholder="选填,仅作记录,不影响流程"></div></div>
|
||
</div>
|
||
</div>
|
||
<div class="dlg-foot">
|
||
<button class="btn" onclick="closeDialog('mask-account')">取 消</button>
|
||
<button class="btn" onclick="saveAccount(false)">保 存</button>
|
||
<button class="btn primary" onclick="saveAccount(true)">核 算</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 选择物流公司弹窗(核算勾选) ===== --><!-- ===== 选择物流公司弹窗(核算勾选) ===== -->
|
||
<div class="mask" id="mask-pick">
|
||
<div class="dialog" style="width:860px;">
|
||
<div class="dlg-head">选择物流公司(单选、选填)<span class="close" onclick="closeDialog('mask-pick')">×</span></div>
|
||
<div class="dlg-body">
|
||
<div class="filter-bar">
|
||
<input class="input" id="pick-kw" placeholder="公司名称 / 统一社会信用代码" style="width:260px;" oninput="renderPickTable()">
|
||
<span class="tag blue">仅显示启用状态公司</span>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="tbl">
|
||
<thead><tr>
|
||
<th style="width:40px;">选择</th>
|
||
<th>公司名称</th><th>统一社会信用代码</th><th>联系人</th><th>联系方式</th><th>生效合同</th>
|
||
</tr></thead>
|
||
<tbody id="pick-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
<div class="pagination"><span id="pick-pg"></span><button class="pg-btn">«</button><button class="pg-btn cur">1</button><button class="pg-btn">»</button><select><option>20条/页</option></select></div>
|
||
<div class="pick-summary" style="margin-bottom:0;">已选择 <span class="count" id="pick-dialog-count">0</span> 家物流公司(单选)</div>
|
||
</div>
|
||
<div class="dlg-foot">
|
||
<button class="btn" onclick="closeDialog('mask-pick')">取 消</button>
|
||
<button class="btn primary" onclick="confirmPick()">确 定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== 通用确认弹窗 ===== -->
|
||
<div class="mask" id="mask-confirm" style="z-index:150;">
|
||
<div class="dialog" style="width:420px; margin-top:180px;">
|
||
<div class="dlg-head">提示<span class="close" onclick="closeDialog('mask-confirm')">×</span></div>
|
||
<div class="dlg-body" id="confirm-msg" style="padding:26px 26px 10px; color:#606266;"></div>
|
||
<div class="dlg-foot">
|
||
<button class="btn" onclick="closeDialog('mask-confirm')">取 消</button>
|
||
<button class="btn primary" id="confirm-ok">确 定</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="toast-wrap"></div>
|
||
|
||
<script>
|
||
/* ================= 演示数据 ================= */
|
||
const TODAY = '2026-09-28';
|
||
const TAX_SOURCES = ['云南运八数据科技有限公司','富源运八数据科技有限公司','曲靖市马龙区呈钢加油站有限公司'];
|
||
let companySeq = 8;
|
||
let companies = [
|
||
{ id:1, name:'云南顺达物流有限公司', shortCode:'SDWL', uscc:'91530100MA6N5X2P7A', legal:'李建国', contact:'李建国', phone:'13888880001',
|
||
region:'云南省 昆明市 官渡区', address:'曙光东路67号', remark:'', status:1, usedInSettlement:true, creator:'super_admin', createdAt:'2026-07-02 10:32:40',
|
||
contracts:[
|
||
{ no:'HT2026-001', tax:0, creator:'super_admin', signDate:'2026-07-02', createDate:'2026-07-08 10:51:14', effDate:'2026-07-09', expDate:'2027-07-08', file:'顺达承运合同.pdf' },
|
||
{ no:'HT2026-002', tax:1, creator:'super_admin', signDate:'2026-05-11', createDate:'2026-05-12 09:20:03', effDate:'2026-05-12', expDate:'2026-08-31', file:'顺达补充协议.pdf' }
|
||
]},
|
||
{ id:2, name:'昆明恒通货运有限公司', shortCode:'HT', uscc:'91530100MA6K8T3Q5B', legal:'王志强', contact:'王志强', phone:'13888880002',
|
||
region:'云南省 昆明市 五华区', address:'学府路118号', remark:'', status:1, usedInSettlement:true, creator:'super_admin', createdAt:'2026-07-08 14:11:26',
|
||
contracts:[
|
||
{ no:'45345453453', tax:0, creator:'super_admin', signDate:'2026-07-15', createDate:'2026-07-08 17:38:59', effDate:'2026-07-14', expDate:'2027-07-14', file:'恒通框架合同.docx' }
|
||
]},
|
||
{ id:3, name:'曲靖晨光物流有限公司', shortCode:'CGL', uscc:'91530300MA6L1M9R3C', legal:'张艳', contact:'张艳', phone:'13888880003',
|
||
region:'云南省 曲靖市 麒麟区', address:'南宁东路22号', remark:'', status:0, usedInSettlement:false, creator:'super_admin', createdAt:'2026-07-11 09:34:07',
|
||
contracts:[
|
||
{ no:'ceshi', tax:2, creator:'super_admin', signDate:'2026-06-02', createDate:'2026-06-08 10:51:14', effDate:'2026-06-09', expDate:'2026-07-31', file:'晨光运输合同.pdf' }
|
||
]},
|
||
{ id:4, name:'富源永顺物流有限公司', shortCode:'YSWL', uscc:'91530325MA6M4B7K2D', legal:'刘伟', contact:'刘伟', phone:'13888880004',
|
||
region:'云南省 曲靖市 富源县', address:'胜境大道9号', remark:'', status:1, usedInSettlement:false, creator:'super_admin', createdAt:'2026-08-01 16:02:44',
|
||
contracts:[] },
|
||
{ id:5, name:'大理苍山运输有限公司', shortCode:'CSYS', uscc:'91532900MA6P9C4X6E', legal:'赵磊', contact:'赵磊', phone:'13888880005',
|
||
region:'云南省 大理州 大理市', address:'人民南路35号', remark:'', status:1, usedInSettlement:false, creator:'super_admin', createdAt:'2026-08-15 11:20:19',
|
||
contracts:[
|
||
{ no:'RH888899', tax:1, creator:'super_admin', signDate:'2026-09-01', createDate:'2026-09-02 10:34:07', effDate:'2026-10-15', expDate:'2027-10-14', file:'苍山运输合同2027.pdf' }
|
||
]},
|
||
{ id:6, name:'玉溪通达物流有限公司', shortCode:'TD', uscc:'91530400MA6Q2D8F1G', legal:'孙明', contact:'孙明', phone:'13888880006',
|
||
region:'云南省 玉溪市 红塔区', address:'凤凰路160号', remark:'', status:1, usedInSettlement:false, creator:'super_admin', createdAt:'2026-09-01 08:45:52',
|
||
contracts:[
|
||
{ no:'TDHT-2026-01', tax:0, creator:'super_admin', signDate:'2026-08-20', createDate:'2026-08-21 15:12:31', effDate:'2026-09-01', expDate:'2026-12-31', file:'通达承运合同.pdf' },
|
||
{ no:'TDHT-2026-02', tax:0, creator:'super_admin', signDate:'2026-09-10', createDate:'2026-09-11 09:05:00', effDate:'2026-09-12', expDate:'2027-09-11', file:'通达线路补充协议.pdf' }
|
||
]},
|
||
{ id:7, name:'保山联运物流有限公司', shortCode:'LYWL', uscc:'91530500MA6R7H9J3H', legal:'陈静', contact:'陈静', phone:'13888880007',
|
||
region:'云南省 保山市 隆阳区', address:'永昌大道88号', remark:'', status:1, usedInSettlement:false, creator:'super_admin', createdAt:'2026-09-12 17:30:08',
|
||
contracts:[
|
||
{ no:'1111', tax:0, creator:'super_admin', signDate:'2026-07-02', createDate:'2026-07-08 10:32:40', effDate:'2026-07-11', expDate:'2026-07-10', file:'联运合同.pdf' }
|
||
]}
|
||
];
|
||
|
||
let currentEditId = 1; // 当前编辑的公司
|
||
let contractEditIdx = null; // 正在编辑的合同下标,null=新增
|
||
let contractTmpFile = ''; // 合同弹窗内模拟附件
|
||
let pickedIds = new Set(); // 选择弹窗中已选公司(单选,最多1个)
|
||
let pickMode = 'account'; // 选择弹窗用途:account=运单核算 / source=货源
|
||
let accountIdx = null; // 当前修改核算的运单下标
|
||
let accountCompanyId = 0; // 修改核算弹窗中已选的物流公司
|
||
let sourceCompanyId = 0; // 货源上选择的物流公司
|
||
let confirmCb = null;
|
||
|
||
/* 打款记录演示数据(companyId=0 表示无外协公司;companyName 为运单记录的快照) */
|
||
let payments = [
|
||
{ no:'DK20260920-001', waybill:'YD2026091800126', companyId:1, companyName:'云南顺达物流有限公司', driver:'李国强', amount:12500.00, status:'已打款', time:'2026-09-20 10:12:36' },
|
||
{ no:'DK20260921-002', waybill:'YD2026091900303', companyId:6, companyName:'玉溪通达物流有限公司', driver:'王志明', amount:8600.50, status:'已打款', time:'2026-09-21 15:40:11' },
|
||
{ no:'DK20260922-003', waybill:'YD2026092000741', companyId:0, companyName:'', driver:'张永发', amount:5300.00, status:'待打款', time:'2026-09-22 09:05:00' },
|
||
{ no:'DK20260923-004', waybill:'YD2026092100215', companyId:2, companyName:'昆明恒通货运有限公司', driver:'刘小波', amount:15980.00, status:'待打款', time:'2026-09-23 14:22:45' },
|
||
{ no:'DK20260924-005', waybill:'YD2026092200966', companyId:1, companyName:'云南顺达物流有限公司', driver:'赵天宇', amount:7450.25, status:'已打款', time:'2026-09-24 11:30:09' },
|
||
{ no:'DK20260925-006', waybill:'YD2026092400482', companyId:0, companyName:'', driver:'孙建军', amount:9820.00, status:'已驳回', time:'2026-09-25 16:08:53' }
|
||
];
|
||
|
||
/* 货源 / 运单演示数据(companyName 为记录的快照;空表示无外协公司) */
|
||
let goodsSources = [
|
||
{ no:'HY20260918-001', goods:'钢材一批', from:'昆明市官渡区', to:'曲靖市麒麟区', companyId:1, companyName:'云南顺达物流有限公司', creator:'super_admin', createdAt:'2026-09-18 09:20:11' },
|
||
{ no:'HY20260919-002', goods:'瓷砖40吨', from:'昆明市五华区', to:'大理市', companyId:6, companyName:'玉溪通达物流有限公司', creator:'super_admin', createdAt:'2026-09-19 10:05:33' },
|
||
{ no:'HY20260920-003', goods:'化肥30吨', from:'曲靖市沾益区', to:'贵阳市修文县', companyId:0, companyName:'', creator:'super_admin', createdAt:'2026-09-20 14:41:02' },
|
||
{ no:'HY20260921-004', goods:'机械设备', from:'玉溪市红塔区', to:'昆明市官渡区', companyId:2, companyName:'昆明恒通货运有限公司', creator:'super_admin', createdAt:'2026-09-21 11:18:47' },
|
||
{ no:'HY20260922-005', goods:'日用百货', from:'昆明市官渡区', to:'保山市隆阳区', companyId:0, companyName:'', creator:'super_admin', createdAt:'2026-09-22 16:02:28' },
|
||
{ no:'HY20260923-006', goods:'铝锭35吨', from:'富源县', to:'昆明市西山区', companyId:1, companyName:'云南顺达物流有限公司', creator:'super_admin', createdAt:'2026-09-23 08:55:14' }
|
||
];
|
||
let waybills = [
|
||
{ no:'YD2026091800126', goodsNo:'HY20260918-001', goods:'钢材一批', driver:'李国强(云A12345)', companyId:1, companyName:'云南顺达物流有限公司', status:'运输中', amount:12500.00 },
|
||
{ no:'YD2026091900303', goodsNo:'HY20260919-002', goods:'瓷砖40吨', driver:'王志明(云A67890)', companyId:6, companyName:'玉溪通达物流有限公司', status:'已卸货', amount:8600.50 },
|
||
{ no:'YD2026092000741', goodsNo:'HY20260920-003', goods:'化肥30吨', driver:'张永发(云C23456)', companyId:0, companyName:'', status:'待运输', amount:5300.00 },
|
||
{ no:'YD2026092100215', goodsNo:'HY20260921-004', goods:'机械设备', driver:'刘小波(云D34567)', companyId:2, companyName:'昆明恒通货运有限公司', status:'已结算', amount:15980.00 },
|
||
{ no:'YD2026092200966', goodsNo:'HY20260922-005', goods:'日用百货', driver:'赵天宇(云F45678)', companyId:0, companyName:'', status:'运输中', amount:7450.25 },
|
||
{ no:'YD2026092400482', goodsNo:'HY20260923-006', goods:'铝锭35吨', driver:'孙建军(云G56789)', companyId:1, companyName:'云南顺达物流有限公司', status:'已卸货', amount:9820.00 }
|
||
];
|
||
|
||
/* ================= 工具 ================= */
|
||
function esc(s){ return String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"'); }
|
||
function todayStr(){ return TODAY; }
|
||
function contractStatus(c){
|
||
if (c.effDate > TODAY) return ['待生效','orange'];
|
||
if (c.expDate >= TODAY) return ['生效中','green'];
|
||
return ['已过期','gray'];
|
||
}
|
||
function activeContractCount(co){ return co.contracts.filter(c=>contractStatus(c)[0]==='生效中').length; }
|
||
function getCompany(id){ return companies.find(c=>c.id===id); }
|
||
|
||
function toast(msg, type){
|
||
const t = document.createElement('div');
|
||
t.className = 'toast ' + (type||'success');
|
||
t.textContent = msg;
|
||
document.getElementById('toast-wrap').appendChild(t);
|
||
setTimeout(()=>t.remove(), 2600);
|
||
}
|
||
function confirmDialog(msg, cb){
|
||
document.getElementById('confirm-msg').innerHTML = msg;
|
||
confirmCb = cb;
|
||
document.getElementById('mask-confirm').classList.add('open');
|
||
}
|
||
document.getElementById('confirm-ok').onclick = function(){
|
||
closeDialog('mask-confirm');
|
||
if (confirmCb) confirmCb();
|
||
};
|
||
function closeDialog(id){ document.getElementById(id).classList.remove('open'); }
|
||
function toggleMenu(id){
|
||
const el = document.getElementById(id);
|
||
el.classList.toggle('open');
|
||
}
|
||
function todo(){ toast('原型未实现该菜单,请通过左侧"物流公司管理"体验','warning'); }
|
||
function mockUploadFile(){
|
||
contractTmpFile = '合同扫描件_' + Math.floor(Math.random()*900+100) + '.pdf';
|
||
document.getElementById('ct-file-name').textContent = contractTmpFile;
|
||
}
|
||
function toggleUpload(box){
|
||
const frame = box.querySelector('.up-frame');
|
||
const done = frame.classList.toggle('done');
|
||
if (done){
|
||
frame.innerHTML = '<span class="plus">+</span><span>✓ 已上传</span><span style="color:#909399;">'+(box.dataset.name||'证照扫描件.jpg')+'</span>';
|
||
frame.querySelector('.plus').style.display='none';
|
||
} else {
|
||
frame.innerHTML = '<span class="plus">+</span>';
|
||
}
|
||
}
|
||
|
||
/* ================= 页面路由 ================= */
|
||
const CRUMBS = {
|
||
home: ['首页'],
|
||
list: ['首页','基础信息','承运管理','物流公司管理'],
|
||
add: ['首页','基础信息','承运管理','物流公司管理','新增物流公司'],
|
||
edit: ['首页','基础信息','承运管理','物流公司管理','编辑物流公司'],
|
||
source: ['首页','整车运输','货源管理','发布货源'],
|
||
settle: ['首页','整车运输','承运结算','运费核算'],
|
||
pay: ['首页','整车运输','承运结算','货主打款 / 财务打款'],
|
||
goodslist: ['首页','整车运输','货源管理','货源列表'],
|
||
waybill: ['首页','整车运输','运输管理','运单管理']
|
||
};
|
||
function go(page){
|
||
document.querySelectorAll('.page').forEach(p=>p.classList.remove('active'));
|
||
document.getElementById('page-'+page).classList.add('active');
|
||
document.querySelectorAll('.chip').forEach(c=>c.classList.toggle('active', c.dataset.page===page));
|
||
document.getElementById('crumb').innerHTML = CRUMBS[page].map((s,i)=>
|
||
i===CRUMBS[page].length-1 ? '<b>'+s+'</b>' : s).join('<span class="sep">/</span>');
|
||
if (page==='list') renderCompanyTable();
|
||
if (page==='add') resetAddForm();
|
||
if (page==='edit') loadEditCompany(currentEditId);
|
||
if (page==='settle') renderAccountList();
|
||
if (page==='source') renderSourcePicked();
|
||
if (page==='pay') renderPaymentTable();
|
||
if (page==='goodslist') renderGoodsTable();
|
||
if (page==='waybill') renderWaybillTable();
|
||
if (page==='home') renderStats();
|
||
window.scrollTo(0,0);
|
||
}
|
||
|
||
/* ================= 公司表单(新增/编辑共用渲染) ================= */
|
||
const FORM_FIELDS = [
|
||
{ key:'name', label:'公司名称', req:1, ph:'请输入公司名称' },
|
||
{ key:'uscc', label:'统一社会信用代码', req:1, ph:'请输入18位统一社会信用代码' },
|
||
{ key:'legal', label:'法人代表名称', req:1, ph:'请输入法人代表名称' },
|
||
{ key:'contact', label:'联系人', req:1, ph:'请输入联系人' },
|
||
{ key:'phone', label:'联系方式', req:1, ph:'请输入联系方式' },
|
||
{ key:'shortCode',label:'简称码', req:0, ph:'请输入2-4位纯字母的物流公司简称' },
|
||
{ key:'region', label:'企业通信地址', req:1, type:'region' },
|
||
{ key:'address', label:'详细地址', req:1, ph:'请输入详细地址' },
|
||
{ key:'remark', label:'备注', req:0, type:'textarea', full:1 }
|
||
];
|
||
const REGIONS = ['','云南省 昆明市 官渡区','云南省 昆明市 五华区','云南省 曲靖市 麒麟区','云南省 曲靖市 富源县','云南省 大理州 大理市','云南省 玉溪市 红塔区','云南省 保山市 隆阳区'];
|
||
function renderForm(containerId, data){
|
||
const box = document.getElementById(containerId);
|
||
box.innerHTML = FORM_FIELDS.map(f=>{
|
||
const val = data ? (data[f.key]||'') : '';
|
||
let ctrl;
|
||
if (f.type==='region'){
|
||
ctrl = '<select class="select" data-key="'+f.key+'">' + REGIONS.map(r=>
|
||
'<option value="'+esc(r)+'"'+(r===val?' selected':'')+'>'+(r||'请选择')+'</option>').join('') + '</select>';
|
||
} else if (f.type==='textarea'){
|
||
ctrl = '<textarea class="textarea" data-key="'+f.key+'" placeholder="'+f.ph+'">'+esc(val)+'</textarea>';
|
||
} else {
|
||
ctrl = '<input class="input" data-key="'+f.key+'" placeholder="'+f.ph+'" value="'+esc(val)+'">';
|
||
}
|
||
return '<div class="form-item'+(f.full?' full':'')+'"><label class="'+(f.req?'req':'')+'">'+f.label+'</label><div class="ctrl">'+ctrl+'</div></div>';
|
||
}).join('');
|
||
}
|
||
function readForm(containerId){
|
||
const out = {};
|
||
document.querySelectorAll('#'+containerId+' [data-key]').forEach(el=>{ out[el.dataset.key] = el.value.trim(); });
|
||
return out;
|
||
}
|
||
function resetAddForm(){ renderForm('add-form', null); }
|
||
function loadEditCompany(id){
|
||
const co = getCompany(id) || companies[0];
|
||
currentEditId = co.id;
|
||
renderForm('edit-form', co);
|
||
document.querySelectorAll('#page-edit .upload-box').forEach(b=>{ if(!b.querySelector('.up-frame.done')) toggleUpload(b); });
|
||
renderContractTable();
|
||
switchTab('profile');
|
||
}
|
||
|
||
/* ================= 保存公司(新增/编辑) ================= */
|
||
function saveCompany(mode){
|
||
const formId = mode==='add' ? 'add-form' : 'edit-form';
|
||
const data = readForm(formId);
|
||
const labelOf = k => FORM_FIELDS.find(f=>f.key===k).label;
|
||
// 必填校验
|
||
for (const f of FORM_FIELDS){
|
||
if (f.req && !data[f.key]){ toast('请填写「'+f.label+'」','error'); return; }
|
||
}
|
||
if (!/^[\dA-HJ-NPQRTUWXY]{18}$/.test(data.uscc)){ toast('统一社会信用代码须为18位(数字/字母,不含I、O、Z、S、V)','error'); return; }
|
||
if (!/^1\d{10}$/.test(data.phone)){ toast('联系方式须为11位手机号','error'); return; }
|
||
if (data.shortCode && !/^[A-Za-z]{2,4}$/.test(data.shortCode)){ toast('简称码须为2-4位纯字母','error'); return; }
|
||
// 证件:营业执照、法人身份证人像面/国徽面必传(道路运输经营许可证选填)
|
||
const page = document.getElementById(mode==='add'?'page-add':'page-edit');
|
||
for (const box of page.querySelectorAll('.upload-box[data-required="1"]')){
|
||
if (!box.querySelector('.up-frame.done')){
|
||
toast('请上传「'+box.querySelector('.up-label').textContent.trim()+'」','error');
|
||
return;
|
||
}
|
||
}
|
||
// 唯一性校验(排除自身)
|
||
const selfId = mode==='add' ? null : currentEditId;
|
||
if (companies.some(c=>c.name===data.name && c.id!==selfId)){ toast('公司名称已存在:'+data.name,'error'); return; }
|
||
if (companies.some(c=>c.uscc===data.uscc && c.id!==selfId)){ toast('统一社会信用代码已存在:'+data.uscc,'error'); return; }
|
||
if (data.shortCode && companies.some(c=>c.shortCode && c.shortCode.toUpperCase()===data.shortCode.toUpperCase() && c.id!==selfId)){
|
||
toast('简称码已存在:'+data.shortCode,'error'); return;
|
||
}
|
||
if (mode==='add'){
|
||
companies.unshift({
|
||
id: companySeq++, ...data, shortCode: data.shortCode.toUpperCase(),
|
||
status:1, usedInSettlement:false, creator:'super_admin',
|
||
createdAt:'2026-09-28 ' + new Date().toTimeString().slice(0,8), contracts:[]
|
||
});
|
||
toast('新增成功,状态默认为启用','success');
|
||
} else {
|
||
Object.assign(getCompany(currentEditId), data, { shortCode: data.shortCode.toUpperCase() });
|
||
toast('保存成功','success');
|
||
}
|
||
go('list');
|
||
}
|
||
|
||
/* ================= 公司列表 ================= */
|
||
function resetQuery(){
|
||
['q-name','q-uscc','q-start','q-end'].forEach(i=>document.getElementById(i).value='');
|
||
document.getElementById('q-status').value='';
|
||
renderCompanyTable();
|
||
}
|
||
function renderCompanyTable(){
|
||
const name = document.getElementById('q-name').value.trim();
|
||
const uscc = document.getElementById('q-uscc').value.trim();
|
||
const st = document.getElementById('q-status').value;
|
||
const s1 = document.getElementById('q-start').value, s2 = document.getElementById('q-end').value;
|
||
const rows = companies.filter(c=>{
|
||
if (name && !c.name.includes(name)) return false;
|
||
if (uscc && !c.uscc.includes(uscc)) return false;
|
||
if (st!=='' && String(c.status)!==st) return false;
|
||
if (s1 && c.createdAt.slice(0,10) < s1) return false;
|
||
if (s2 && c.createdAt.slice(0,10) > s2) return false;
|
||
return true;
|
||
});
|
||
const tbody = document.getElementById('company-tbody');
|
||
if (!rows.length){
|
||
tbody.innerHTML = '<tr><td colspan="12" class="empty">未找到符合条件的物流公司</td></tr>';
|
||
} else {
|
||
tbody.innerHTML = rows.map(c=>`<tr>
|
||
<td><input type="checkbox" class="ck-company"></td>
|
||
<td><span class="name-link" onclick="editCompany(${c.id})">${esc(c.name)}</span></td>
|
||
<td>${esc(c.shortCode||'-')}</td>
|
||
<td>${esc(c.uscc)}</td>
|
||
<td>${esc(c.legal)}</td>
|
||
<td>${esc(c.contact)}</td>
|
||
<td>${esc(c.phone)}</td>
|
||
<td>${activeContractCount(c)}</td>
|
||
<td><span class="switch ${c.status?'on':''}" onclick="toggleStatus(${c.id})" title="${c.status?'启用':'停用'}"></span>
|
||
<span class="tag ${c.status?'green':'gray'}" style="margin-left:6px;">${c.status?'启用':'停用'}</span></td>
|
||
<td>${esc(c.creator)}</td>
|
||
<td>${esc(c.createdAt)}</td>
|
||
<td><span class="link" onclick="editCompany(${c.id})">编辑</span><span class="link danger" onclick="delCompany(${c.id})">删除</span></td>
|
||
</tr>`).join('');
|
||
}
|
||
document.getElementById('pg-info').textContent = '共 '+rows.length+' 条记录';
|
||
document.getElementById('ck-all-company').checked = false;
|
||
renderStats();
|
||
}
|
||
function toggleAllCompany(box){ document.querySelectorAll('.ck-company').forEach(c=>c.checked=box.checked); }
|
||
function editCompany(id){ currentEditId = id; go('edit'); }
|
||
function toggleStatus(id){
|
||
const co = getCompany(id);
|
||
if (co.status){
|
||
confirmDialog('确定停用物流公司「'+esc(co.name)+'」吗?<br><span style="color:#909399;font-size:12px;">停用后将不再出现在运费核算勾选列表,历史核算单不受影响。</span>', ()=>{
|
||
co.status = 0; renderCompanyTable(); toast('已停用','success');
|
||
});
|
||
} else {
|
||
co.status = 1; renderCompanyTable(); toast('已启用','success');
|
||
}
|
||
}
|
||
function delCompany(id){
|
||
const co = getCompany(id);
|
||
const activeN = activeContractCount(co);
|
||
if (co.usedInSettlement || activeN>0){
|
||
const reasons = [];
|
||
if (activeN>0) reasons.push('存在 '+activeN+' 份生效中合同');
|
||
if (co.usedInSettlement) reasons.push('已被核算单引用');
|
||
toast('删除失败:该公司'+reasons.join(',')+',无法删除,可改为停用','error');
|
||
return;
|
||
}
|
||
confirmDialog('确定删除物流公司「'+esc(co.name)+'」吗?<br><span style="color:#909399;font-size:12px;">删除后不可恢复(逻辑删除)。</span>', ()=>{
|
||
companies = companies.filter(c=>c.id!==id);
|
||
pickedIds.delete(id);
|
||
renderCompanyTable(); toast('删除成功','success');
|
||
});
|
||
}
|
||
|
||
/* ================= 编辑页 Tab ================= */
|
||
function switchTab(name){
|
||
document.querySelectorAll('#page-edit .tab-item').forEach(t=>t.classList.toggle('active', t.dataset.tab===name));
|
||
document.querySelectorAll('#page-edit .tab-panel').forEach(p=>p.classList.remove('active'));
|
||
document.getElementById('tab-'+name).classList.add('active');
|
||
if (name==='contract') renderContractTable();
|
||
}
|
||
|
||
/* ================= 合同管理 ================= */
|
||
function initTaxSelects(){
|
||
document.getElementById('cf-tax').innerHTML = '<option value="">请选择税源地</option>' + TAX_SOURCES.map((t,i)=>'<option value="'+i+'">'+esc(t)+'</option>').join('');
|
||
document.getElementById('ct-tax').innerHTML = '<option value="">请选择</option>' + TAX_SOURCES.map((t,i)=>'<option value="'+i+'">'+esc(t)+'</option>').join('');
|
||
}
|
||
function renderContractTable(){
|
||
const co = getCompany(currentEditId);
|
||
const no = document.getElementById('cf-no').value.trim();
|
||
const tax = document.getElementById('cf-tax').value;
|
||
const creator = document.getElementById('cf-creator').value.trim();
|
||
const sg1 = document.getElementById('cf-sign1').value, sg2 = document.getElementById('cf-sign2').value;
|
||
const rows = co.contracts.map((c,idx)=>({c,idx})).filter(({c})=>{
|
||
if (no && !c.no.includes(no)) return false;
|
||
if (tax!=='' && String(c.tax)!==tax) return false;
|
||
if (creator && !c.creator.includes(creator)) return false;
|
||
if (sg1 && c.signDate < sg1) return false;
|
||
if (sg2 && c.signDate > sg2) return false;
|
||
return true;
|
||
});
|
||
const tbody = document.getElementById('contract-tbody');
|
||
if (!rows.length){
|
||
tbody.innerHTML = '<tr><td colspan="10" class="empty">暂无合同,点击"新增"录入</td></tr>';
|
||
} else {
|
||
tbody.innerHTML = rows.map(({c,idx})=>{
|
||
const [st, color] = contractStatus(c);
|
||
return `<tr>
|
||
<td><input type="checkbox" class="ck-contract"></td>
|
||
<td>${esc(c.no)}</td>
|
||
<td>${esc(TAX_SOURCES[c.tax])}</td>
|
||
<td><span class="tag ${color}">${st}</span></td>
|
||
<td>${esc(c.creator)}</td>
|
||
<td>${esc(c.signDate)} 00:00:00</td>
|
||
<td>${esc(c.createDate)}</td>
|
||
<td>${esc(c.effDate)}</td>
|
||
<td>${esc(c.expDate)}</td>
|
||
<td><span class="link" onclick="viewContract(${idx})">查看</span><span class="link" onclick="openContractDialog('edit',${idx})">编辑</span><span class="link danger" onclick="delContract(${idx})">删除</span></td>
|
||
</tr>`;
|
||
}).join('');
|
||
}
|
||
document.getElementById('cpg-info').textContent = '共 '+rows.length+' 条记录';
|
||
}
|
||
function toggleAllContract(box){ document.querySelectorAll('.ck-contract').forEach(c=>c.checked=box.checked); }
|
||
|
||
function openContractDialog(mode, idx){
|
||
contractEditIdx = mode==='edit' ? idx : null;
|
||
document.getElementById('contract-dlg-title').textContent = mode==='edit' ? '编辑合同' : '新增';
|
||
const co = getCompany(currentEditId);
|
||
const c = mode==='edit' ? co.contracts[idx] : null;
|
||
document.getElementById('ct-no').value = c ? c.no : '';
|
||
document.getElementById('ct-sign').value = c ? c.signDate : '';
|
||
document.getElementById('ct-eff').value = c ? c.effDate : '';
|
||
document.getElementById('ct-exp').value = c ? c.expDate : '';
|
||
document.getElementById('ct-tax').value = c ? String(c.tax) : '';
|
||
contractTmpFile = c ? c.file : '';
|
||
document.getElementById('ct-file-name').textContent = contractTmpFile;
|
||
document.getElementById('err-ct-no').style.display='none';
|
||
document.getElementById('err-ct-date').style.display='none';
|
||
document.getElementById('mask-contract').classList.add('open');
|
||
}
|
||
function saveContract(){
|
||
const co = getCompany(currentEditId);
|
||
const no = document.getElementById('ct-no').value.trim();
|
||
const sign = document.getElementById('ct-sign').value;
|
||
const eff = document.getElementById('ct-eff').value;
|
||
const exp = document.getElementById('ct-exp').value;
|
||
const tax = document.getElementById('ct-tax').value;
|
||
if (!no){ toast('请输入合同编号','error'); return; }
|
||
if (!sign){ toast('请选择签订时间','error'); return; }
|
||
if (!eff){ toast('请选择生效时间','error'); return; }
|
||
if (!exp){ toast('请选择过期时间','error'); return; }
|
||
if (exp <= eff){ toast('过期时间必须晚于生效时间','error'); return; }
|
||
if (tax===''){ toast('请选择所属税源地','error'); return; }
|
||
if (!contractTmpFile){ toast('请上传合同附件(pdf/doc/docx/jpg/jpeg/bmp/png)','error'); return; }
|
||
if (co.contracts.some((c,i)=>c.no===no && i!==contractEditIdx)){ toast('同一公司下合同编号已存在:'+no,'error'); return; }
|
||
if (contractEditIdx===null){
|
||
co.contracts.unshift({ no, tax:Number(tax), creator:'super_admin', signDate:sign,
|
||
createDate:'2026-09-28 ' + new Date().toTimeString().slice(0,8), effDate:eff, expDate:exp, file:contractTmpFile });
|
||
toast('合同新增成功','success');
|
||
} else {
|
||
Object.assign(co.contracts[contractEditIdx], { no, tax:Number(tax), signDate:sign, effDate:eff, expDate:exp, file:contractTmpFile });
|
||
toast('合同保存成功','success');
|
||
}
|
||
closeDialog('mask-contract');
|
||
renderContractTable();
|
||
}
|
||
function viewContract(idx){
|
||
const co = getCompany(currentEditId);
|
||
const c = co.contracts[idx];
|
||
const [st, color] = contractStatus(c);
|
||
document.getElementById('view-body').innerHTML = `
|
||
<div class="dlg-form">
|
||
<div class="form-item"><label>合同编号</label><div class="ctrl" style="line-height:32px;">${esc(c.no)}</div></div>
|
||
<div class="form-item"><label>合同状态</label><div class="ctrl"><span class="tag ${color}">${st}</span></div></div>
|
||
<div class="form-item"><label>所属税源地</label><div class="ctrl" style="line-height:32px;">${esc(TAX_SOURCES[c.tax])}</div></div>
|
||
<div class="form-item"><label>签订时间</label><div class="ctrl" style="line-height:32px;">${esc(c.signDate)}</div></div>
|
||
<div class="form-item"><label>生效时间</label><div class="ctrl" style="line-height:32px;">${esc(c.effDate)}</div></div>
|
||
<div class="form-item"><label>过期时间</label><div class="ctrl" style="line-height:32px;">${esc(c.expDate)}</div></div>
|
||
<div class="form-item"><label>附件</label><div class="ctrl"><span class="link">📎 ${esc(c.file)}</span><span class="tip">预览 / 下载</span></div></div>
|
||
</div>`;
|
||
document.getElementById('mask-view').classList.add('open');
|
||
}
|
||
function delContract(idx){
|
||
const co = getCompany(currentEditId);
|
||
const c = co.contracts[idx];
|
||
confirmDialog('确定删除合同「'+esc(c.no)+'」吗?', ()=>{
|
||
co.contracts.splice(idx,1);
|
||
renderContractTable(); toast('合同已删除','success');
|
||
});
|
||
}
|
||
|
||
/* ================= 选择物流公司弹窗(单选、选填:运单核算 / 货源) ================= */
|
||
function openPickDialog(mode){
|
||
pickMode = mode || 'account';
|
||
pickedIds.clear();
|
||
if (pickMode==='source' && sourceCompanyId) pickedIds.add(sourceCompanyId);
|
||
if (pickMode==='account' && accountCompanyId) pickedIds.add(accountCompanyId);
|
||
renderPickTable();
|
||
document.getElementById('mask-pick').classList.add('open');
|
||
}
|
||
function renderPickTable(){
|
||
const kw = document.getElementById('pick-kw').value.trim();
|
||
const rows = companies.filter(c=>c.status===1)
|
||
.filter(c=>!kw || c.name.includes(kw) || c.uscc.includes(kw));
|
||
const tbody = document.getElementById('pick-tbody');
|
||
if (!rows.length){
|
||
tbody.innerHTML = '<tr><td colspan="6" class="empty">未找到符合条件的物流公司</td></tr>';
|
||
} else {
|
||
tbody.innerHTML = rows.map(c=>{
|
||
const n = activeContractCount(c);
|
||
return `<tr>
|
||
<td><input type="radio" name="pick-radio" class="ck-pick" data-id="${c.id}" ${pickedIds.has(c.id)?'checked':''} onchange="pickChange(this)"></td>
|
||
<td>${esc(c.name)}</td>
|
||
<td>${esc(c.uscc)}</td>
|
||
<td>${esc(c.contact)}</td>
|
||
<td>${esc(c.phone)}</td>
|
||
<td>${n>0 ? n+' 份生效中' : '<span class="warn-txt">⚠ 无生效合同</span>'}</td>
|
||
</tr>`;
|
||
}).join('');
|
||
}
|
||
document.getElementById('pick-pg').textContent = '共 '+rows.length+' 条记录';
|
||
document.getElementById('pick-dialog-count').textContent = pickedIds.size;
|
||
}
|
||
function pickChange(box){
|
||
const id = Number(box.dataset.id);
|
||
if (box.checked){ pickedIds.clear(); pickedIds.add(id); } // 单选:替换上次选择
|
||
else pickedIds.delete(id);
|
||
document.getElementById('pick-dialog-count').textContent = pickedIds.size;
|
||
}
|
||
function confirmPick(){
|
||
closeDialog('mask-pick');
|
||
if (pickMode==='account'){
|
||
accountCompanyId = pickedIds.size ? [...pickedIds][0] : 0;
|
||
renderAccountPicked();
|
||
toast(accountCompanyId ? '已选择物流公司' : '已清除所选物流公司','success');
|
||
} else {
|
||
sourceCompanyId = pickedIds.size ? [...pickedIds][0] : 0;
|
||
renderSourcePicked();
|
||
toast(sourceCompanyId ? '已选择物流公司' : '已清除所选物流公司','success');
|
||
}
|
||
}
|
||
/* ================= 运费核算(在运单上核算,FR-07) ================= */
|
||
function renderAccountList(){
|
||
const noEl = document.getElementById('af-no');
|
||
const stEl = document.getElementById('af-status');
|
||
const kw = noEl ? noEl.value.trim() : '';
|
||
const st = stEl ? stEl.value : '';
|
||
const rows = waybills.map((w,i)=>({w,i})).filter(({w})=>(!kw || w.no.includes(kw)) && (!st || (st==='已核算') === !!w.accounted));
|
||
const tbody = document.getElementById('acc-tbody');
|
||
tbody.innerHTML = rows.map(({w,i})=>`<tr>
|
||
<td>${esc(w.no)}</td>
|
||
<td>${esc(w.goodsNo)}</td>
|
||
<td>${esc(w.goods)}</td>
|
||
<td>${esc(w.driver)}</td>
|
||
<td>${w.companyId ? esc(w.companyName) : '<span class="tip">无外协公司</span>'}</td>
|
||
<td>${w.accounted ? '<span class="tag green">已核算</span>' : '<span class="tag red">未核算</span>'}</td>
|
||
<td>${w.addr ? esc(w.addr) : '<span class="tip">-</span>'}</td>
|
||
<td><span class="link" onclick="openAccountDialog(${i})">修改核算</span></td>
|
||
</tr>`).join('');
|
||
document.getElementById('acc-pg').textContent = '共 '+rows.length+' 条记录';
|
||
}
|
||
function openAccountDialog(i){
|
||
accountIdx = i;
|
||
const w = waybills[i];
|
||
document.getElementById('acc-no').value = w.no;
|
||
document.getElementById('acc-goods').value = w.goods;
|
||
document.getElementById('acc-driver').value = w.driver;
|
||
accountCompanyId = w.companyId || 0;
|
||
renderAccountPicked();
|
||
document.getElementById('acc-addr').value = w.addr || '';
|
||
document.getElementById('mask-account').classList.add('open');
|
||
}
|
||
function renderAccountPicked(){
|
||
const tbody = document.getElementById('acc-picked-tbody');
|
||
const co = accountCompanyId ? getCompany(accountCompanyId) : null;
|
||
tbody.innerHTML = co
|
||
? `<tr><td>${esc(co.name)}</td><td>${esc(co.uscc)}</td><td><span class="link danger" onclick="clearAccountCompany()">移除</span></td></tr>`
|
||
: '<tr><td colspan="3" class="empty">未选择物流公司(无外协公司)</td></tr>';
|
||
}
|
||
function clearAccountCompany(){ accountCompanyId = 0; renderAccountPicked(); }
|
||
function saveAccount(account){
|
||
const w = waybills[accountIdx];
|
||
const addr = document.getElementById('acc-addr').value.trim();
|
||
if (addr.length > 200){ toast('客户收货地址不能超过200字符','error'); return; }
|
||
const co = accountCompanyId ? getCompany(accountCompanyId) : null;
|
||
if (co && co.status !== 1){ toast('保存失败:所选公司已被停用,请重新选择 —— '+co.name,'error'); return; }
|
||
w.companyId = accountCompanyId;
|
||
w.companyName = co ? co.name : '';
|
||
w.addr = addr;
|
||
if (account) w.accounted = true;
|
||
closeDialog('mask-account');
|
||
renderAccountList();
|
||
toast(account ? '核算完成:核算数据已写入运单' : '已保存:核算数据已写入运单','success');
|
||
}
|
||
|
||
/* ================= 货源选择物流公司(FR-08) ================= */
|
||
function renderSourcePicked(){
|
||
const tbody = document.getElementById('src-picked-tbody');
|
||
const co = sourceCompanyId ? getCompany(sourceCompanyId) : null;
|
||
if (!co){
|
||
tbody.innerHTML = '<tr><td colspan="4" class="empty">未选择物流公司(选填)</td></tr>';
|
||
return;
|
||
}
|
||
tbody.innerHTML = `<tr>
|
||
<td>${esc(co.name)}</td><td>${esc(co.uscc)}</td><td>${esc(co.contact)}</td>
|
||
<td><span class="link danger" onclick="removeSourcePicked()">移除</span></td>
|
||
</tr>`;
|
||
}
|
||
function removeSourcePicked(){ sourceCompanyId = 0; renderSourcePicked(); }
|
||
function saveSource(){
|
||
const goods = document.getElementById('src-goods').value.trim();
|
||
if (!goods){ toast('请填写「货物名称」','error'); return; }
|
||
const co = sourceCompanyId ? getCompany(sourceCompanyId) : null;
|
||
if (co && co.status!==1){ toast('保存失败:所选公司已被停用,请重新选择 —— '+co.name,'error'); return; }
|
||
toast('货源保存成功(演示):物流公司仅作运单记录'+(co?('('+co.name+')'):'(未选择)'),'success');
|
||
go('home');
|
||
}
|
||
|
||
let payTab = 'owner';
|
||
function gotoPay(t){ payTab = t; go('pay'); switchPayTab(t); }
|
||
function switchPayTab(t){ payTab = t; document.querySelectorAll('#page-pay .tab-item').forEach(x=>x.classList.toggle('active', x.dataset.paytab===t)); renderPaymentTable(); }
|
||
function goodsNoOf(p){ const wb = waybills.find(x=>x.no===p.waybill); return wb ? wb.goodsNo : '-'; }
|
||
/* ================= 打款列表(FR-09:物流公司筛选) ================= */
|
||
function initPayFilter(){
|
||
const sel = document.getElementById('pf-company');
|
||
if (sel.options.length) return;
|
||
sel.innerHTML = '<option value="">全部</option><option value="0">无外协公司</option>'
|
||
+ companies.map(c=>'<option value="'+c.id+'">'+esc(c.name)+(c.status?'':'(停用)')+'</option>').join('');
|
||
}
|
||
function renderPaymentTable(){
|
||
initPayFilter();
|
||
const cid = document.getElementById('pf-company').value;
|
||
const wb = document.getElementById('pf-waybill').value.trim();
|
||
const st = document.getElementById('pf-status').value;
|
||
const rows = payments.filter(p=>{
|
||
if (p.type !== payTab) return false;
|
||
if (cid !== ''){
|
||
const c = Number(cid);
|
||
if (c === 0){ if (p.companyId) return false; } // 无外协公司
|
||
else if (p.companyId !== c) return false;
|
||
}
|
||
if (wb && !p.waybill.includes(wb)) return false;
|
||
if (st && p.status !== st) return false;
|
||
return true;
|
||
});
|
||
const tbody = document.getElementById('pay-tbody');
|
||
if (!rows.length){
|
||
tbody.innerHTML = '<tr><td colspan="7" class="empty">未找到符合条件的打款记录</td></tr>';
|
||
} else {
|
||
const tagMap = {'已打款':'green','待打款':'orange','已驳回':'gray'};
|
||
tbody.innerHTML = rows.map(p=>`<tr>
|
||
<td>${esc(p.waybill)}</td>
|
||
<td>${esc(goodsNoOf(p))}</td>
|
||
<td>${esc(p.waybill)}</td>
|
||
<td>${p.companyId ? esc(p.companyName) : '<span class="tip">无外协公司</span>'}</td>
|
||
<td>${esc(p.driver)}</td>
|
||
<td>${p.amount.toFixed(2)}</td>
|
||
<td><span class="tag ${tagMap[p.status]||'gray'}">${esc(p.status)}</span></td>
|
||
<td>${esc(p.time)}</td>
|
||
</tr>`).join('');
|
||
}
|
||
document.getElementById('pay-pg').textContent = '共 '+rows.length+' 条记录';
|
||
}
|
||
function resetPayQuery(){
|
||
document.getElementById('pf-company').value='';
|
||
document.getElementById('pf-waybill').value='';
|
||
document.getElementById('pf-status').value='';
|
||
renderPaymentTable();
|
||
}
|
||
|
||
/* ================= 货源列表 / 运单管理(FR-10:显示物流公司列) ================= */
|
||
function renderGoodsTable(){
|
||
const kw = document.getElementById('gf-goods').value.trim();
|
||
const no = document.getElementById('gf-no').value.trim();
|
||
const rows = goodsSources.filter(g=>(!kw || g.goods.includes(kw)) && (!no || g.no.includes(no)));
|
||
const tbody = document.getElementById('goods-tbody');
|
||
if (!rows.length){
|
||
tbody.innerHTML = '<tr><td colspan="8" class="empty">未找到符合条件的货源</td></tr>';
|
||
} else {
|
||
tbody.innerHTML = rows.map(g=>`<tr>
|
||
<td>${esc(g.no)}</td>
|
||
<td>${esc(g.goods)}</td>
|
||
<td>${esc(g.from)}</td>
|
||
<td>${esc(g.to)}</td>
|
||
<td>${g.companyId ? esc(g.companyName) : '<span class="tip">无外协公司</span>'}</td>
|
||
<td>${esc(g.creator)}</td>
|
||
<td>${esc(g.createdAt)}</td>
|
||
<td><span class="link" onclick="todo()">编辑</span></td>
|
||
</tr>`).join('');
|
||
}
|
||
document.getElementById('goods-pg').textContent = '共 '+rows.length+' 条记录';
|
||
}
|
||
function resetGoodsQuery(){
|
||
document.getElementById('gf-goods').value='';
|
||
document.getElementById('gf-no').value='';
|
||
renderGoodsTable();
|
||
}
|
||
function renderWaybillTable(){
|
||
const no = document.getElementById('wf-no').value.trim();
|
||
const kw = document.getElementById('wf-goods').value.trim();
|
||
const rows = waybills.filter(w=>(!no || w.no.includes(no)) && (!kw || w.goods.includes(kw)));
|
||
const tbody = document.getElementById('waybill-tbody');
|
||
if (!rows.length){
|
||
tbody.innerHTML = '<tr><td colspan="7" class="empty">未找到符合条件的运单</td></tr>';
|
||
} else {
|
||
const tagMap = {'待运输':'gray','运输中':'blue','已卸货':'orange','已结算':'green'};
|
||
tbody.innerHTML = rows.map(w=>`<tr>
|
||
<td>${esc(w.no)}</td>
|
||
<td>${esc(w.goodsNo)}</td>
|
||
<td>${esc(w.goods)}</td>
|
||
<td>${esc(w.driver)}</td>
|
||
<td>${w.companyId ? esc(w.companyName) : '<span class="tip">无外协公司</span>'}</td>
|
||
<td><span class="tag ${tagMap[w.status]||'gray'}">${esc(w.status)}</span></td>
|
||
<td>${w.amount.toFixed(2)}</td>
|
||
</tr>`).join('');
|
||
}
|
||
document.getElementById('waybill-pg').textContent = '共 '+rows.length+' 条记录';
|
||
}
|
||
function resetWaybillQuery(){
|
||
document.getElementById('wf-no').value='';
|
||
document.getElementById('wf-goods').value='';
|
||
renderWaybillTable();
|
||
}
|
||
/* ================= 统计 ================= */
|
||
function renderStats(){
|
||
document.getElementById('stat-total').textContent = companies.length;
|
||
document.getElementById('stat-active').textContent = companies.filter(c=>c.status===1).length;
|
||
document.getElementById('stat-contracts').textContent = companies.reduce((s,c)=>s+activeContractCount(c),0);
|
||
}
|
||
|
||
/* ================= 初始化 ================= */
|
||
initTaxSelects();
|
||
waybills.forEach(w=>{ if (w.accounted === undefined){ w.accounted = false; w.addr = ''; } });
|
||
const PAY_TYPES = {'DK20260920-001':'owner','DK20260921-002':'finance','DK20260922-003':'owner',
|
||
'DK20260923-004':'finance','DK20260924-005':'owner','DK20260925-006':'finance'};
|
||
payments.forEach(p=>{ p.type = PAY_TYPES[p.no] || 'owner'; });
|
||
go('home');
|
||
</script>
|
||
</body>
|
||
</html>
|