feat: 管理端场所/分类管理增强及小程序端交互优化
- 后台营养管理:场所新增/编辑页重构(营业时段/图片/资质)、食材及餐品分类管理弹窗、从已有食材复制 - 营养管理小程序:净菜包订单按状态差异化展示、时间选择器交互重构、新增收货地址页、移除营养目标分区 - CHANGELOG 更新至 2026-07-23 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
bab86f72f3
commit
ea8239debf
@@ -13,6 +13,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
--radius-sm:4px;--radius-md:8px;--radius-lg:12px;
|
||||
--shadow-sm:0 1px 4px rgba(0,0,0,0.08);--shadow-md:0 4px 12px rgba(0,0,0,0.12);
|
||||
}
|
||||
/* ===== 顶部导航 ===== */
|
||||
.top-bar{display:flex;align-items:center;height:56px;background:#001529;padding:0 24px;position:fixed;top:0;left:0;right:0;z-index:100;}
|
||||
.top-bar__logo{display:flex;align-items:center;gap:8px;color:#fff;font-size:var(--font-lg);font-weight:600;white-space:nowrap;width:240px;flex-shrink:0;}
|
||||
.top-bar__logo svg{width:28px;height:28px;}
|
||||
@@ -24,6 +25,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.top-bar__right .btn-back{color:rgba(255,255,255,0.65);cursor:pointer;display:flex;align-items:center;gap:4px;text-decoration:none;}
|
||||
.top-bar__right .btn-back:hover{color:#fff;}
|
||||
.avatar{width:32px;height:32px;border-radius:50%;background:#1890FF;display:flex;align-items:center;justify-content:center;color:#fff;font-size:var(--font-sm);cursor:pointer;}
|
||||
/* ===== 侧边栏 ===== */
|
||||
.sidebar{position:fixed;top:56px;left:0;bottom:0;width:240px;background:#001529;overflow-y:auto;z-index:90;padding-top:8px;}
|
||||
.sidebar-menu__title{color:rgba(255,255,255,0.35);font-size:var(--font-xs);padding:16px 24px 8px;text-transform:uppercase;letter-spacing:.5px;}
|
||||
.sidebar-item{display:flex;align-items:center;gap:8px;padding:10px 24px;color:rgba(255,255,255,0.65);font-size:var(--font-sm);cursor:pointer;transition:all .15s;text-decoration:none;}
|
||||
@@ -36,15 +38,21 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.sidebar-subgroup.open .sidebar-subgroup__header .arrow{transform:rotate(90deg);}
|
||||
.sidebar-subgroup__body{display:none;padding-left:12px;}
|
||||
.sidebar-subgroup.open .sidebar-subgroup__body{display:block;}
|
||||
/* ===== 主内容 ===== */
|
||||
.main{margin-left:240px;margin-top:56px;padding:24px;min-height:calc(100vh - 56px);}
|
||||
.breadcrumb{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);color:var(--text-secondary);margin-bottom:16px;}
|
||||
.breadcrumb a{color:var(--primary);text-decoration:none;}.breadcrumb a:hover{text-decoration:underline;}
|
||||
.breadcrumb__sep{color:var(--text-placeholder);}
|
||||
/* ===== 表单卡片 ===== */
|
||||
.form-card{background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);padding:24px;margin-bottom:16px;}
|
||||
.form-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:20px;padding-bottom:12px;border-bottom:1px solid var(--border);}
|
||||
.form-card__title{font-size:var(--font-base);font-weight:600;margin-bottom:20px;padding-bottom:12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:8px;}
|
||||
.form-card__title .card-icon{font-size:18px;}
|
||||
.form-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px 24px;}
|
||||
.form-grid.col3{grid-template-columns:repeat(3,1fr);}
|
||||
.form-grid.col4{grid-template-columns:repeat(4,1fr);}
|
||||
.form-group{display:flex;flex-direction:column;gap:6px;}
|
||||
.form-group.full{grid-column:1/-1;}
|
||||
.form-group.span2{grid-column:span 2;}
|
||||
.form-label{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.form-label .required{color:var(--danger);margin-left:2px;}
|
||||
.form-input,.form-select{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;width:100%;}
|
||||
@@ -52,16 +60,22 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.form-input:disabled{background:#F5F5F5;color:var(--text-placeholder);cursor:not-allowed;}
|
||||
.form-select{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;padding-right:28px;}
|
||||
.form-hint{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;}
|
||||
.form-textarea{min-height:80px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 12px;font-size:var(--font-sm);color:var(--text-primary);outline:none;background:#fff;width:100%;resize:vertical;font-family:inherit;}
|
||||
.form-textarea:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.1);}
|
||||
/* ===== 按钮 ===== */
|
||||
.btn{height:36px;padding:0 16px;border-radius:var(--radius-sm);font-size:var(--font-sm);cursor:pointer;border:none;transition:all .2s;display:inline-flex;align-items:center;gap:6px;text-decoration:none;}
|
||||
.btn-primary{background:var(--primary);color:#fff;}.btn-primary:hover{background:#40a9ff;}
|
||||
.btn-default{background:#fff;color:var(--text-primary);border:1px solid var(--border);}.btn-default:hover{color:var(--primary);border-color:var(--primary);}
|
||||
.btn-sm{height:28px;padding:0 10px;font-size:var(--font-xs);}
|
||||
.btn-text{background:none;border:none;color:var(--primary);cursor:pointer;font-size:var(--font-sm);padding:0;}.btn-text:hover{color:#40a9ff;}
|
||||
.btn-text--danger{color:var(--danger);}.btn-text--danger:hover{color:#ff7875;}
|
||||
.form-footer{display:flex;align-items:center;gap:12px;padding:16px 24px;background:#fff;border-radius:var(--radius-md);box-shadow:var(--shadow-sm);}
|
||||
/* 地图区域 */
|
||||
.map-container{width:100%;height:320px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#E8ECF0;position:relative;overflow:hidden;}
|
||||
.map-placeholder{width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-placeholder);gap:8px;background:linear-gradient(135deg,#e8ecf0 25%,#dde2e8 25%,#dde2e8 50%,#e8ecf0 50%,#e8ecf0 75%,#dde2e8 75%);background-size:20px 20px;}
|
||||
.map-placeholder__icon{font-size:40px;opacity:0.5;}
|
||||
.map-placeholder__text{font-size:var(--font-sm);}
|
||||
/* 模拟地图内容 */
|
||||
/* ===== 单选按钮组 ===== */
|
||||
.radio-group{display:flex;gap:20px;height:36px;align-items:center;}
|
||||
.radio-item{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);cursor:pointer;}
|
||||
.radio-item input[type="radio"]{width:16px;height:16px;accent-color:var(--primary);}
|
||||
/* ===== 地图区域 ===== */
|
||||
.map-container{width:100%;height:280px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#E8ECF0;position:relative;overflow:hidden;}
|
||||
.map-mock{width:100%;height:100%;position:relative;background:#e8edf2;}
|
||||
.map-mock__grid{position:absolute;inset:0;background-image:linear-gradient(rgba(0,0,0,0.05) 1px,transparent 1px),linear-gradient(90deg,rgba(0,0,0,0.05) 1px,transparent 1px);background-size:40px 40px;}
|
||||
.map-mock__roads{position:absolute;inset:0;}
|
||||
@@ -76,14 +90,61 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.map-mock__search{position:absolute;left:12px;top:12px;display:flex;gap:6px;z-index:5;}
|
||||
.map-mock__search input{width:220px;height:32px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-xs);background:#fff;box-shadow:var(--shadow-sm);}
|
||||
.map-mock__search button{height:32px;padding:0 12px;background:var(--primary);color:#fff;border:none;border-radius:var(--radius-sm);font-size:var(--font-xs);cursor:pointer;}
|
||||
/* 单选按钮组 */
|
||||
.radio-group{display:flex;gap:20px;height:36px;align-items:center;}
|
||||
.radio-item{display:flex;align-items:center;gap:6px;font-size:var(--font-sm);cursor:pointer;}
|
||||
.radio-item input[type="radio"]{width:16px;height:16px;accent-color:var(--primary);}
|
||||
/* ===== 营业时段 ===== */
|
||||
.biz-hours-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.biz-hours-table th{text-align:left;padding:8px 10px;font-weight:500;color:var(--text-secondary);font-size:var(--font-xs);border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
.biz-hours-table th .required{color:var(--danger);}
|
||||
.biz-hours-table td{padding:8px 10px;border-bottom:1px solid #f5f5f5;vertical-align:middle;}
|
||||
.biz-hours-table .col-meal{width:100px;}
|
||||
.biz-hours-table .col-weekday{min-width:260px;}
|
||||
.biz-hours-table .col-time{width:110px;}
|
||||
.biz-hours-table .col-action{width:50px;text-align:center;}
|
||||
.biz-hours-table .form-input,.biz-hours-table .form-select{height:30px;font-size:var(--font-xs);}
|
||||
.biz-hours-table input[type="time"]{height:30px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 8px;font-size:var(--font-xs);color:var(--text-primary);outline:none;width:108px;}
|
||||
.biz-hours-table input[type="time"]:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.1);}
|
||||
/* 星期切换按钮 */
|
||||
.weekday-toggles{display:flex;gap:4px;flex-wrap:wrap;}
|
||||
.weekday-toggle{width:30px;height:28px;border-radius:var(--radius-sm);border:1px solid var(--border);background:#fff;font-size:var(--font-xs);color:var(--text-secondary);cursor:pointer;transition:all .15s;display:flex;align-items:center;justify-content:center;user-select:none;}
|
||||
.weekday-toggle:hover{border-color:var(--primary);color:var(--primary);}
|
||||
.weekday-toggle.active{background:var(--primary);color:#fff;border-color:var(--primary);}
|
||||
.weekday-toggle-all{width:auto;padding:0 8px;font-size:var(--font-xs);color:var(--primary);cursor:pointer;user-select:none;height:28px;display:flex;align-items:center;}
|
||||
.biz-hours-add{margin-top:12px;}
|
||||
/* ===== 图片上传区 ===== */
|
||||
.upload-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px;}
|
||||
.upload-zone{border:2px dashed var(--border);border-radius:var(--radius-md);padding:32px 16px;text-align:center;cursor:pointer;transition:all .2s;background:#FAFAFA;min-height:180px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;position:relative;overflow:hidden;}
|
||||
.upload-zone:hover{border-color:var(--primary);background:var(--primary-light);}
|
||||
.upload-zone.has-image{border-style:solid;border-color:var(--success);background:#F6FFED;padding:0;}
|
||||
.upload-zone__icon{width:48px;height:48px;border-radius:50%;background:#E8E8E8;display:flex;align-items:center;justify-content:center;font-size:22px;color:var(--text-placeholder);}
|
||||
.upload-zone:hover .upload-zone__icon{background:var(--primary-light);color:var(--primary);}
|
||||
.upload-zone__text{font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.upload-zone__hint{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.upload-zone__label{position:absolute;top:0;left:0;background:var(--danger);color:#fff;font-size:10px;padding:3px 10px;border-radius:0 0 var(--radius-sm) 0;font-weight:500;}
|
||||
.upload-zone__preview{width:100%;height:100%;object-fit:cover;display:block;}
|
||||
.upload-zone__overlay{position:absolute;inset:0;background:rgba(0,0,0,0.45);display:flex;align-items:center;justify-content:center;gap:8px;opacity:0;transition:opacity .2s;}
|
||||
.upload-zone.has-image:hover .upload-zone__overlay{opacity:1;}
|
||||
.upload-zone__overlay .btn{background:#fff;color:var(--text-primary);height:28px;padding:0 12px;font-size:var(--font-xs);}
|
||||
/* ===== 资质信息 ===== */
|
||||
.qual-section{margin-bottom:20px;padding-bottom:20px;border-bottom:1px dashed var(--border);}
|
||||
.qual-section:last-child{margin-bottom:0;padding-bottom:0;border-bottom:none;}
|
||||
.qual-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;display:flex;align-items:center;gap:6px;}
|
||||
.qual-section__title .dot{width:6px;height:6px;border-radius:50%;background:var(--primary);}
|
||||
.qual-section__title .tag-optional{font-size:var(--font-xs);color:var(--text-placeholder);font-weight:400;margin-left:4px;}
|
||||
.qual-img-row{display:flex;gap:16px;margin-top:12px;}
|
||||
.qual-img-item{flex:1;max-width:240px;}
|
||||
.qual-img-item .upload-zone-small{border:2px dashed var(--border);border-radius:var(--radius-sm);min-height:120px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;cursor:pointer;background:#FAFAFA;transition:all .2s;}
|
||||
.qual-img-item .upload-zone-small:hover{border-color:var(--primary);background:var(--primary-light);}
|
||||
.qual-img-item .upload-zone-small__icon{font-size:28px;color:var(--text-placeholder);}
|
||||
.qual-img-item .upload-zone-small__text{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.qual-img-item .upload-zone-small__label{font-size:var(--font-xs);color:var(--text-secondary);margin-bottom:2px;}
|
||||
/* ===== 日期范围 ===== */
|
||||
.date-range{display:flex;align-items:center;gap:8px;}
|
||||
.date-range input[type="date"]{height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 10px;font-size:var(--font-sm);color:var(--text-primary);outline:none;width:150px;}
|
||||
.date-range input[type="date"]:focus{border-color:var(--primary);box-shadow:0 0 0 2px rgba(24,144,255,0.1);}
|
||||
.date-range__sep{color:var(--text-placeholder);font-size:var(--font-sm);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部导航 -->
|
||||
<!-- ===== 顶部导航 ===== -->
|
||||
<header class="top-bar">
|
||||
<div class="top-bar__logo">
|
||||
<svg viewBox="0 0 28 28" fill="none"><rect width="28" height="28" rx="6" fill="#1890FF"/><path d="M7 14h14M14 7v14" stroke="#fff" stroke-width="2.5" stroke-linecap="round"/></svg>
|
||||
@@ -105,7 +166,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- 侧边栏 -->
|
||||
<!-- ===== 侧边栏 ===== -->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-menu__title">营养用餐</div>
|
||||
<a href="din-canteen.html" class="sidebar-item active"><span class="mi">🏢</span>场所管理</a>
|
||||
@@ -145,18 +206,18 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<a href="din-innovation.html" class="sidebar-item"><span class="mi">🌟</span>味养创新</a>
|
||||
</aside>
|
||||
|
||||
<!-- 主内容 -->
|
||||
<!-- ===== 主内容 ===== -->
|
||||
<main class="main">
|
||||
<!-- 面包屑 -->
|
||||
<div class="breadcrumb">
|
||||
<a href="din-canteen.html">场所管理</a>
|
||||
<span class="breadcrumb__sep">/</span>
|
||||
<span>新增场所</span>
|
||||
<span id="pageTitle">新增场所</span>
|
||||
</div>
|
||||
|
||||
<!-- 基本信息表单 -->
|
||||
<!-- 卡片1:基本信息 -->
|
||||
<div class="form-card">
|
||||
<div class="form-card__title">基本信息</div>
|
||||
<div class="form-card__title"><span class="card-icon">📋</span>基本信息</div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label class="form-label">场所名称<span class="required">*</span></label>
|
||||
@@ -165,7 +226,6 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<div class="form-group">
|
||||
<label class="form-label">场所编号</label>
|
||||
<input class="form-input" value="系统自动生成" disabled>
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">场所类型<span class="required">*</span></label>
|
||||
@@ -188,11 +248,11 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">负责人</label>
|
||||
<label class="form-label">负责人<span class="required">*</span></label>
|
||||
<input class="form-input" placeholder="请输入负责人姓名">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">负责人电话</label>
|
||||
<label class="form-label">负责人电话<span class="required">*</span></label>
|
||||
<input class="form-input" placeholder="请输入联系电话">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -205,15 +265,12 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 地址信息 -->
|
||||
<!-- 卡片2:地址信息 -->
|
||||
<div class="form-card">
|
||||
<div class="form-card__title">地址信息</div>
|
||||
<!-- 地图选点 -->
|
||||
<div class="map-container">
|
||||
<div class="form-card__title"><span class="card-icon">📍</span>地址信息</div>
|
||||
<div class="map-container" style="margin-bottom:12px;">
|
||||
<div class="map-mock">
|
||||
<!-- 网格背景 -->
|
||||
<div class="map-mock__grid"></div>
|
||||
<!-- 模拟道路 -->
|
||||
<div class="map-mock__roads">
|
||||
<div class="map-mock__road-h" style="top:35%"></div>
|
||||
<div class="map-mock__road-h" style="top:55%"></div>
|
||||
@@ -222,17 +279,14 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<div class="map-mock__road-v" style="left:50%"></div>
|
||||
<div class="map-mock__road-v" style="left:72%"></div>
|
||||
</div>
|
||||
<!-- 搜索框 -->
|
||||
<div class="map-mock__search">
|
||||
<input placeholder="搜索地址或地名">
|
||||
<button>搜索</button>
|
||||
</div>
|
||||
<!-- 缩放控件 -->
|
||||
<div class="map-mock__controls">
|
||||
<div class="map-mock__ctrl-btn">+</div>
|
||||
<div class="map-mock__ctrl-btn">−</div>
|
||||
</div>
|
||||
<!-- 标记点 -->
|
||||
<div class="map-mock__pin">
|
||||
<svg viewBox="0 0 36 48" fill="none">
|
||||
<path d="M18 0C8.06 0 0 8.06 0 18c0 13.5 18 30 18 30s18-16.5 18-30C36 8.06 27.94 0 18 0z" fill="#FF4D4F"/>
|
||||
@@ -243,11 +297,155 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
<div class="map-mock__label">📍 点击地图选择位置</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top:12px;">
|
||||
<div class="form-group">
|
||||
<label class="form-label">详细地址</label>
|
||||
<input class="form-input" id="addressInput" placeholder="地图选点后自动填入,也可手动输入详细地址">
|
||||
<span class="form-hint">点击地图选点后,地址信息将自动写入此输入框</span>
|
||||
<div class="form-group">
|
||||
<label class="form-label">详细地址<span class="required">*</span></label>
|
||||
<input class="form-input" id="addressInput" placeholder="地图选点后自动填入,也可手动输入详细地址">
|
||||
<span class="form-hint">点击地图选点后,地址信息将自动写入此输入框</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片3:营业时段 -->
|
||||
<div class="form-card">
|
||||
<div class="form-card__title"><span class="card-icon">🕐</span>营业时段</div>
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="biz-hours-table" id="bizHoursTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-meal">餐次<span class="required">*</span></th>
|
||||
<th class="col-weekday">营业日<span class="required">*</span></th>
|
||||
<th class="col-time">开始时间<span class="required">*</span></th>
|
||||
<th class="col-time">结束时间<span class="required">*</span></th>
|
||||
<th class="col-action">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="bizHoursBody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="biz-hours-add">
|
||||
<button class="btn btn-default btn-sm" onclick="addBizHourRow()">+ 添加时段</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片4:场所图片 -->
|
||||
<div class="form-card">
|
||||
<div class="form-card__title"><span class="card-icon">🖼️</span>场所图片</div>
|
||||
<div class="upload-grid">
|
||||
<!-- 场所Logo -->
|
||||
<div class="upload-zone" id="uploadLogo" onclick="simulateUpload('uploadLogo')">
|
||||
<span class="upload-zone__label">必填</span>
|
||||
<div class="upload-zone__icon">🏢</div>
|
||||
<div class="upload-zone__text">场所Logo</div>
|
||||
<div class="upload-zone__hint">建议尺寸 200×200,支持 jpg/png</div>
|
||||
<div class="upload-zone__overlay">
|
||||
<span class="btn" onclick="event.stopPropagation();simulateUpload('uploadLogo')">重新上传</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 经营场所门头照 -->
|
||||
<div class="upload-zone" id="uploadStorefront" onclick="simulateUpload('uploadStorefront')">
|
||||
<span class="upload-zone__label">必填</span>
|
||||
<div class="upload-zone__icon">📷</div>
|
||||
<div class="upload-zone__text">经营场所门头照</div>
|
||||
<div class="upload-zone__hint">建议尺寸 800×600,支持 jpg/png</div>
|
||||
<div class="upload-zone__overlay">
|
||||
<span class="btn" onclick="event.stopPropagation();simulateUpload('uploadStorefront')">重新上传</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 场所环境照 -->
|
||||
<div class="upload-zone" id="uploadEnvironment" onclick="simulateUpload('uploadEnvironment')">
|
||||
<span class="upload-zone__label">必填</span>
|
||||
<div class="upload-zone__icon">🖼️</div>
|
||||
<div class="upload-zone__text">场所环境照</div>
|
||||
<div class="upload-zone__hint">建议尺寸 800×600,支持 jpg/png</div>
|
||||
<div class="upload-zone__overlay">
|
||||
<span class="btn" onclick="event.stopPropagation();simulateUpload('uploadEnvironment')">重新上传</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 卡片5:资质信息 -->
|
||||
<div class="form-card">
|
||||
<div class="form-card__title"><span class="card-icon">📜</span>资质信息</div>
|
||||
|
||||
<!-- 营业执照 -->
|
||||
<div class="qual-section">
|
||||
<div class="qual-section__title"><span class="dot"></span>营业执照<span class="required" style="color:var(--danger);">*</span></div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label class="form-label">营业执照照片<span class="required">*</span></label>
|
||||
<div class="qual-img-row">
|
||||
<div class="qual-img-item">
|
||||
<div class="upload-zone-small" id="uploadBizLicense" onclick="simulateUpload('uploadBizLicense')">
|
||||
<div class="upload-zone-small__icon">📄</div>
|
||||
<div class="upload-zone-small__text">点击上传营业执照</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">统一社会信用代码<span class="required">*</span></label>
|
||||
<input class="form-input" placeholder="请输入统一社会信用代码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">有效期<span class="required">*</span></label>
|
||||
<div class="date-range">
|
||||
<input type="date" value="2023-01-01">
|
||||
<span class="date-range__sep">至</span>
|
||||
<input type="date" value="2033-01-01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 食品经营许可证 -->
|
||||
<div class="qual-section">
|
||||
<div class="qual-section__title"><span class="dot"></span>食品经营许可证<span class="required" style="color:var(--danger);">*</span></div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group">
|
||||
<label class="form-label">许可证照片<span class="required">*</span></label>
|
||||
<div class="qual-img-row">
|
||||
<div class="qual-img-item">
|
||||
<div class="upload-zone-small" id="uploadFoodPermit" onclick="simulateUpload('uploadFoodPermit')">
|
||||
<div class="upload-zone-small__icon">📄</div>
|
||||
<div class="upload-zone-small__text">点击上传食品经营许可证</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">许可证编号<span class="required">*</span></label>
|
||||
<input class="form-input" placeholder="请输入许可证编号">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">有效期<span class="required">*</span></label>
|
||||
<div class="date-range">
|
||||
<input type="date" value="2023-06-01">
|
||||
<span class="date-range__sep">至</span>
|
||||
<input type="date" value="2028-06-01">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 法人身份证 -->
|
||||
<div class="qual-section">
|
||||
<div class="qual-section__title"><span class="dot"></span>法人身份证<span class="tag-optional">(非必填)</span></div>
|
||||
<div class="qual-img-row" style="margin-top:8px;">
|
||||
<div class="qual-img-item">
|
||||
<div class="upload-zone-small__label">身份证正面(人像面)</div>
|
||||
<div class="upload-zone-small" id="uploadIdFront" onclick="simulateUpload('uploadIdFront')">
|
||||
<div class="upload-zone-small__icon">🪪</div>
|
||||
<div class="upload-zone-small__text">点击上传身份证正面</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="qual-img-item">
|
||||
<div class="upload-zone-small__label">身份证反面(国徽面)</div>
|
||||
<div class="upload-zone-small" id="uploadIdBack" onclick="simulateUpload('uploadIdBack')">
|
||||
<div class="upload-zone-small__icon">🪪</div>
|
||||
<div class="upload-zone-small__text">点击上传身份证反面</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -260,15 +458,23 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
</main>
|
||||
|
||||
<script>
|
||||
/* ===== 侧边栏 ===== */
|
||||
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
|
||||
(function(){var active=document.querySelector('.sidebar-item.active');if(!active)return;var sub=active.closest('.sidebar-subgroup');if(sub)sub.classList.add('open');})();
|
||||
|
||||
/* 模拟地图选点交互 */
|
||||
/* ===== 编辑模式检测 ===== */
|
||||
(function(){
|
||||
var params=new URLSearchParams(window.location.search);
|
||||
if(params.get('id')){
|
||||
document.getElementById('pageTitle').textContent='编辑场所';
|
||||
}
|
||||
})();
|
||||
|
||||
/* ===== 地图选点 ===== */
|
||||
document.querySelector('.map-mock').addEventListener('click',function(e){
|
||||
var rect=this.getBoundingClientRect();
|
||||
var x=e.clientX-rect.left;
|
||||
var y=e.clientY-rect.top;
|
||||
/* 移动标记点 */
|
||||
var pin=this.querySelector('.map-mock__pin');
|
||||
var label=this.querySelector('.map-mock__label');
|
||||
pin.style.left=x+'px';
|
||||
@@ -276,13 +482,195 @@ document.querySelector('.map-mock').addEventListener('click',function(e){
|
||||
label.style.left=x+'px';
|
||||
label.style.top=(y+6)+'px';
|
||||
label.textContent='📍 已选择位置';
|
||||
/* 模拟填入地址 */
|
||||
document.getElementById('addressInput').value='陕西省西安市未央区凤城八路CQ能源总部基地';
|
||||
});
|
||||
|
||||
/* ===== 营业时段 ===== */
|
||||
var mealOptions=['早餐','午餐','晚餐','加餐'];
|
||||
|
||||
/* 星期切换 */
|
||||
function toggleWeekday(el){
|
||||
el.classList.toggle('active');
|
||||
}
|
||||
|
||||
/* 全选/取消全选 */
|
||||
function toggleAllWeekday(container){
|
||||
var toggles=container.querySelectorAll('.weekday-toggle');
|
||||
var allActive=true;
|
||||
toggles.forEach(function(t){if(!t.classList.contains('active'))allActive=false;});
|
||||
toggles.forEach(function(t){
|
||||
if(allActive){t.classList.remove('active');}
|
||||
else{t.classList.add('active');}
|
||||
});
|
||||
}
|
||||
|
||||
/* 创建星期按钮组 */
|
||||
function createWeekdayToggles(){
|
||||
var days=['一','二','三','四','五','六','日'];
|
||||
var html='<div class="weekday-toggles">';
|
||||
html+='<span class="weekday-toggle-all" onclick="toggleAllWeekday(this.parentElement)">全选</span>';
|
||||
days.forEach(function(d){
|
||||
html+='<span class="weekday-toggle active" onclick="toggleWeekday(this)">'+d+'</span>';
|
||||
});
|
||||
html+='</div>';
|
||||
return html;
|
||||
}
|
||||
|
||||
/* 创建营业时段行 */
|
||||
function createBizHourRow(meal,start,end){
|
||||
var tr=document.createElement('tr');
|
||||
var mealSelect='<select class="form-select" style="width:90px;">';
|
||||
mealSelect+='<option value="">请选择</option>';
|
||||
mealOptions.forEach(function(m){
|
||||
mealSelect+='<option value="'+m+'"'+(m===meal?' selected':'')+'>'+m+'</option>';
|
||||
});
|
||||
mealSelect+='</select>';
|
||||
|
||||
tr.innerHTML=
|
||||
'<td>'+mealSelect+'</td>'+
|
||||
'<td>'+createWeekdayToggles()+'</td>'+
|
||||
'<td><input type="time" value="'+(start||'07:00')+'"></td>'+
|
||||
'<td><input type="time" value="'+(end||'09:00')+'"></td>'+
|
||||
'<td style="text-align:center;"><button class="btn btn-text btn-text--danger btn-sm" onclick="deleteBizHourRow(this)">删除</button></td>';
|
||||
return tr;
|
||||
}
|
||||
|
||||
/* 添加时段行 */
|
||||
function addBizHourRow(meal,start,end){
|
||||
var tbody=document.getElementById('bizHoursBody');
|
||||
tbody.appendChild(createBizHourRow(meal,start,end));
|
||||
}
|
||||
|
||||
/* 删除时段行 */
|
||||
function deleteBizHourRow(btn){
|
||||
var tbody=document.getElementById('bizHoursBody');
|
||||
if(tbody.children.length<=1){
|
||||
alert('至少保留一个营业时段');
|
||||
return;
|
||||
}
|
||||
btn.closest('tr').remove();
|
||||
}
|
||||
|
||||
/* 初始化默认时段 */
|
||||
(function(){
|
||||
addBizHourRow('早餐','07:00','09:00');
|
||||
addBizHourRow('午餐','11:30','13:00');
|
||||
addBizHourRow('晚餐','17:30','19:00');
|
||||
})();
|
||||
|
||||
/* ===== 模拟图片上传 ===== */
|
||||
function simulateUpload(zoneId){
|
||||
var zone=document.getElementById(zoneId);
|
||||
var label=zone.querySelector('.upload-zone__label');
|
||||
var icon=zone.querySelector('.upload-zone__icon');
|
||||
var text=zone.querySelector('.upload-zone__text');
|
||||
var hint=zone.querySelector('.upload-zone__hint');
|
||||
|
||||
/* 模拟:已有图片则提示重新上传 */
|
||||
if(zone.classList.contains('has-image')){
|
||||
zone.classList.remove('has-image');
|
||||
if(icon)icon.style.display='';
|
||||
if(text)text.style.display='';
|
||||
if(hint)hint.style.display='';
|
||||
if(label)label.style.display='';
|
||||
var existingImg=zone.querySelector('.upload-zone__preview');
|
||||
if(existingImg)existingImg.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
/* 模拟上传成功 */
|
||||
zone.classList.add('has-image');
|
||||
if(icon)icon.style.display='none';
|
||||
if(text)text.style.display='none';
|
||||
if(hint)hint.style.display='none';
|
||||
if(label)label.style.display='none';
|
||||
|
||||
/* 生成模拟预览占位图(SVG 内联) */
|
||||
var svgNS='http://www.w3.org/2000/svg';
|
||||
var colors={uploadLogo:'#1890FF',uploadStorefront:'#52C41A',uploadEnvironment:'#FAAD14',
|
||||
uploadBizLicense:'#722ED1',uploadFoodPermit:'#EB2F96',uploadIdFront:'#13C2C2',uploadIdBack:'#FA541C'};
|
||||
var labels={uploadLogo:'场所Logo',uploadStorefront:'门头照',uploadEnvironment:'环境照',
|
||||
uploadBizLicense:'营业执照',uploadFoodPermit:'食品经营许可证',uploadIdFront:'身份证正面',uploadIdBack:'身份证反面'};
|
||||
var color=colors[zoneId]||'#1890FF';
|
||||
var labelText=labels[zoneId]||'图片';
|
||||
|
||||
var img=document.createElement('div');
|
||||
img.className='upload-zone__preview';
|
||||
img.style.cssText='width:100%;height:100%;display:flex;align-items:center;justify-content:center;background:'+color+'15;';
|
||||
img.innerHTML='<div style="text-align:center;color:'+color+';">'+
|
||||
'<div style="font-size:40px;margin-bottom:6px;">📷</div>'+
|
||||
'<div style="font-size:12px;">'+labelText+'(模拟已上传)</div>'+
|
||||
'</div>';
|
||||
zone.appendChild(img);
|
||||
}
|
||||
|
||||
/* ===== 保存 ===== */
|
||||
function handleSave(){
|
||||
alert('【交互说明】点击保存后,校验必填项,通过后保存并返回场所列表页');
|
||||
/* 校验必填项 */
|
||||
var placeName=document.querySelector('.form-card .form-grid .form-input[placeholder="请输入场所名称"]');
|
||||
var leader=document.querySelector('.form-card .form-grid .form-input[placeholder="请输入负责人姓名"]');
|
||||
var phone=document.querySelector('.form-card .form-grid .form-input[placeholder="请输入联系电话"]');
|
||||
var address=document.getElementById('addressInput');
|
||||
|
||||
if(!placeName.value.trim()){
|
||||
alert('请输入场所名称');placeName.focus();return;
|
||||
}
|
||||
if(!leader.value.trim()){
|
||||
alert('请输入负责人');leader.focus();return;
|
||||
}
|
||||
if(!phone.value.trim()){
|
||||
alert('请输入负责人电话');phone.focus();return;
|
||||
}
|
||||
if(!/^1\d{10}$/.test(phone.value.trim())){
|
||||
alert('请输入正确的手机号码');phone.focus();return;
|
||||
}
|
||||
if(!address.value.trim()){
|
||||
alert('请输入详细地址');address.focus();return;
|
||||
}
|
||||
|
||||
/* 校验营业时段必填 */
|
||||
var rows=document.getElementById('bizHoursBody').querySelectorAll('tr');
|
||||
for(var i=0;i<rows.length;i++){
|
||||
var cells=rows[i].querySelectorAll('td');
|
||||
var mealSelect=cells[0].querySelector('select');
|
||||
var timeStart=cells[2].querySelector('input');
|
||||
var timeEnd=cells[3].querySelector('input');
|
||||
var weekdays=cells[1].querySelectorAll('.weekday-toggle.active');
|
||||
if(!mealSelect.value){
|
||||
alert('请选择第'+(i+1)+'行餐次名称');mealSelect.focus();return;
|
||||
}
|
||||
if(weekdays.length===0){
|
||||
alert('请选择第'+(i+1)+'行营业日');return;
|
||||
}
|
||||
if(!timeStart.value){
|
||||
alert('请填写第'+(i+1)+'行开始时间');timeStart.focus();return;
|
||||
}
|
||||
if(!timeEnd.value){
|
||||
alert('请填写第'+(i+1)+'行结束时间');timeEnd.focus();return;
|
||||
}
|
||||
}
|
||||
|
||||
/* 校验场所图片 */
|
||||
if(!document.getElementById('uploadLogo').classList.contains('has-image')){
|
||||
alert('请上传场所Logo');return;
|
||||
}
|
||||
if(!document.getElementById('uploadStorefront').classList.contains('has-image')){
|
||||
alert('请上传经营场所门头照');return;
|
||||
}
|
||||
if(!document.getElementById('uploadEnvironment').classList.contains('has-image')){
|
||||
alert('请上传场所环境照');return;
|
||||
}
|
||||
|
||||
/* 校验资质信息 */
|
||||
if(!document.getElementById('uploadBizLicense').querySelector('.upload-zone-small').classList.contains('has-image')){
|
||||
alert('请上传营业执照照片');return;
|
||||
}
|
||||
if(!document.getElementById('uploadFoodPermit').querySelector('.upload-zone-small').classList.contains('has-image')){
|
||||
alert('请上传食品经营许可证照片');return;
|
||||
}
|
||||
|
||||
alert('【交互说明】校验通过,保存场所信息并返回列表页');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -79,6 +79,28 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
.detail-row{display:flex;margin-bottom:18px;}
|
||||
.detail-label{width:110px;flex-shrink:0;font-size:var(--font-sm);color:var(--text-secondary);}
|
||||
.detail-value{font-size:var(--font-sm);color:var(--text-primary);flex:1;}
|
||||
/* 详情-分区标题 */
|
||||
.detail-section{border-top:1px dashed var(--border);padding-top:18px;margin-top:18px;}
|
||||
.detail-section:first-child{border-top:none;padding-top:0;margin-top:0;}
|
||||
.detail-section__title{font-size:var(--font-sm);font-weight:600;color:var(--text-primary);margin-bottom:14px;display:flex;align-items:center;gap:6px;}
|
||||
.detail-section__title .dot{width:6px;height:6px;border-radius:50%;background:var(--primary);}
|
||||
/* 详情-营业时段 */
|
||||
.detail-biz-item{display:flex;align-items:center;gap:10px;padding:6px 10px;background:#FAFAFA;border-radius:var(--radius-sm);margin-bottom:8px;font-size:var(--font-xs);}
|
||||
.detail-biz-item .biz-meal{font-weight:600;color:var(--text-primary);min-width:56px;}
|
||||
.detail-biz-item .biz-weekdays{display:flex;gap:2px;}
|
||||
.detail-biz-item .biz-wd{width:22px;height:20px;border-radius:3px;display:flex;align-items:center;justify-content:center;font-size:10px;background:#E8E8E8;color:var(--text-placeholder);}
|
||||
.detail-biz-item .biz-wd.active{background:var(--primary);color:#fff;}
|
||||
.detail-biz-item .biz-time{color:var(--primary);white-space:nowrap;}
|
||||
/* 详情-图片缩略图 */
|
||||
.detail-img-row{display:flex;gap:12px;flex-wrap:wrap;}
|
||||
.detail-img-item{width:100px;text-align:center;}
|
||||
.detail-img-item .thumb{width:100px;height:75px;border-radius:var(--radius-sm);background:#E8ECF0;display:flex;align-items:center;justify-content:center;font-size:28px;color:var(--text-placeholder);border:1px solid var(--border);}
|
||||
.detail-img-item .thumb-label{font-size:10px;color:var(--text-secondary);margin-top:4px;}
|
||||
/* 详情-资质卡片 */
|
||||
.detail-qual-card{background:#FAFAFA;border-radius:var(--radius-sm);padding:12px;margin-bottom:10px;}
|
||||
.detail-qual-card__title{font-size:var(--font-xs);font-weight:600;color:var(--text-primary);margin-bottom:8px;}
|
||||
.detail-qual-card__row{display:flex;font-size:var(--font-xs);margin-bottom:4px;}
|
||||
.detail-qual-card__label{color:var(--text-secondary);width:90px;flex-shrink:0;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -179,10 +201,10 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</div>
|
||||
<div class="table-card__content">
|
||||
<table>
|
||||
<thead><tr><th>场所编号</th><th>场所名称</th><th>场所类型</th><th>所属单位</th><th>详细地址</th><th>操作</th></tr></thead>
|
||||
<thead><tr><th>场所编号</th><th>场所名称</th><th>场所类型</th><th>所属单位</th><th>负责人</th><th>详细地址</th><th>操作</th></tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>ST-2026-001</td><td>总部一号食堂</td><td><span class="tag tag-blue">食堂</span></td><td>CQ能源总部</td><td>陕西省西安市未央区凤城八路总部基地A区</td>
|
||||
<td>ST-2026-001</td><td>总部一号食堂</td><td><span class="tag tag-blue">食堂</span></td><td>CQ能源总部</td><td>张建国</td><td>陕西省西安市未央区凤城八路总部基地A区</td>
|
||||
<td class="td-actions">
|
||||
<span class="link" onclick="openDrawer(0)">详情</span>
|
||||
<a class="link" href="din-canteen-line.html?id=1&name=总部一号食堂">餐线管理</a>
|
||||
@@ -191,7 +213,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ST-2026-002</td><td>总部二号食堂</td><td><span class="tag tag-blue">食堂</span></td><td>CQ能源总部</td><td>陕西省西安市未央区凤城八路总部基地B区</td>
|
||||
<td>ST-2026-002</td><td>总部二号食堂</td><td><span class="tag tag-blue">食堂</span></td><td>CQ能源总部</td><td>李红梅</td><td>陕西省西安市未央区凤城八路总部基地B区</td>
|
||||
<td class="td-actions">
|
||||
<span class="link" onclick="openDrawer(1)">详情</span>
|
||||
<a class="link" href="din-canteen-line.html?id=2&name=总部二号食堂">餐线管理</a>
|
||||
@@ -200,7 +222,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ST-2026-003</td><td>采油一厂职工食堂</td><td><span class="tag tag-blue">食堂</span></td><td>采油一厂</td><td>陕西省延安市安塞区采油一厂生活区</td>
|
||||
<td>ST-2026-003</td><td>采油一厂职工食堂</td><td><span class="tag tag-blue">食堂</span></td><td>采油一厂</td><td>王志强</td><td>陕西省延安市安塞区采油一厂生活区</td>
|
||||
<td class="td-actions">
|
||||
<span class="link" onclick="openDrawer(2)">详情</span>
|
||||
<a class="link" href="din-canteen-line.html?id=3&name=采油一厂职工食堂">餐线管理</a>
|
||||
@@ -209,7 +231,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ST-2026-004</td><td>采油二厂综合食堂</td><td><span class="tag tag-blue">食堂</span></td><td>采油二厂</td><td>甘肃省庆阳市庆城县采油二厂驻地</td>
|
||||
<td>ST-2026-004</td><td>采油二厂综合食堂</td><td><span class="tag tag-blue">食堂</span></td><td>采油二厂</td><td>刘文静</td><td>甘肃省庆阳市庆城县采油二厂驻地</td>
|
||||
<td class="td-actions">
|
||||
<span class="link" onclick="openDrawer(3)">详情</span>
|
||||
<a class="link" href="din-canteen-line.html?id=4&name=采油二厂综合食堂">餐线管理</a>
|
||||
@@ -218,7 +240,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>ST-2026-005</td><td>采气一厂营养餐厅</td><td><span class="tag tag-green">餐厅</span></td><td>采气一厂</td><td>陕西省榆林市靖边县采气一厂作业区</td>
|
||||
<td>ST-2026-005</td><td>采气一厂营养餐厅</td><td><span class="tag tag-green">餐厅</span></td><td>采气一厂</td><td>陈伟</td><td>陕西省榆林市靖边县采气一厂作业区</td>
|
||||
<td class="td-actions">
|
||||
<span class="link" onclick="openDrawer(4)">详情</span>
|
||||
<a class="link" href="din-canteen-line.html?id=5&name=采气一厂营养餐厅">餐线管理</a>
|
||||
@@ -249,24 +271,121 @@ function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
|
||||
|
||||
/* 详情数据 */
|
||||
var canteens=[
|
||||
{name:'总部一号食堂',code:'ST-2026-001',type:'食堂',unit:'CQ能源总部',address:'陕西省西安市未央区凤城八路总部基地A区',leader:'张建国',phone:'138****1001',terminal:'是'},
|
||||
{name:'总部二号食堂',code:'ST-2026-002',type:'食堂',unit:'CQ能源总部',address:'陕西省西安市未央区凤城八路总部基地B区',leader:'李红梅',phone:'139****2002',terminal:'是'},
|
||||
{name:'采油一厂职工食堂',code:'ST-2026-003',type:'食堂',unit:'采油一厂',address:'陕西省延安市安塞区采油一厂生活区',leader:'王志强',phone:'137****3003',terminal:'否'},
|
||||
{name:'采油二厂综合食堂',code:'ST-2026-004',type:'食堂',unit:'采油二厂',address:'甘肃省庆阳市庆城县采油二厂驻地',leader:'刘文静',phone:'136****4004',terminal:'是'},
|
||||
{name:'采气一厂营养餐厅',code:'ST-2026-005',type:'餐厅',unit:'采气一厂',address:'陕西省榆林市靖边县采气一厂作业区',leader:'陈伟',phone:'135****5005',terminal:'否'}
|
||||
{name:'总部一号食堂',code:'ST-2026-001',type:'食堂',unit:'CQ能源总部',address:'陕西省西安市未央区凤城八路总部基地A区',leader:'张建国',phone:'138****1001',terminal:'是',
|
||||
bizHours:[{meal:'早餐',weekdays:[1,1,1,1,1,0,0],start:'07:00',end:'09:00'},{meal:'午餐',weekdays:[1,1,1,1,1,1,0],start:'11:30',end:'13:00'},{meal:'晚餐',weekdays:[1,1,1,1,1,1,1],start:'17:30',end:'19:00'}],
|
||||
images:{logo:true,storefront:true,environment:true},
|
||||
qual:{bizLicense:{code:'91610132MA6TXBXXXX',validFrom:'2023-01-01',validTo:'2033-01-01'},foodPermit:{code:'JY16101234567890',validFrom:'2023-06-01',validTo:'2028-06-01'},idCard:{front:true,back:true}}
|
||||
},
|
||||
{name:'总部二号食堂',code:'ST-2026-002',type:'食堂',unit:'CQ能源总部',address:'陕西省西安市未央区凤城八路总部基地B区',leader:'李红梅',phone:'139****2002',terminal:'是',
|
||||
bizHours:[{meal:'早餐',weekdays:[1,1,1,1,1,0,0],start:'06:30',end:'08:30'},{meal:'午餐',weekdays:[1,1,1,1,1,1,0],start:'11:00',end:'13:30'},{meal:'晚餐',weekdays:[1,1,1,1,1,1,1],start:'17:00',end:'20:00'},{meal:'加餐',weekdays:[1,1,1,1,1,1,1],start:'21:00',end:'23:00'}],
|
||||
images:{logo:true,storefront:true,environment:true},
|
||||
qual:{bizLicense:{code:'91610132MA6TYCYYYY',validFrom:'2022-06-01',validTo:'2032-06-01'},foodPermit:{code:'JY16109876543210',validFrom:'2022-08-01',validTo:'2027-08-01'},idCard:{front:true,back:true}}
|
||||
},
|
||||
{name:'采油一厂职工食堂',code:'ST-2026-003',type:'食堂',unit:'采油一厂',address:'陕西省延安市安塞区采油一厂生活区',leader:'王志强',phone:'137****3003',terminal:'否',
|
||||
bizHours:[{meal:'早餐',weekdays:[1,1,1,1,1,0,0],start:'07:00',end:'09:00'},{meal:'午餐',weekdays:[1,1,1,1,1,0,0],start:'11:30',end:'13:00'},{meal:'晚餐',weekdays:[1,1,1,1,1,0,0],start:'17:30',end:'19:00'}],
|
||||
images:{logo:true,storefront:true,environment:false},
|
||||
qual:{bizLicense:{code:'91610624MA6TXZZZZZ',validFrom:'2023-03-01',validTo:'2033-03-01'},foodPermit:{code:'JY16105555666',validFrom:'2023-03-15',validTo:'2028-03-15'},idCard:{front:false,back:false}}
|
||||
},
|
||||
{name:'采油二厂综合食堂',code:'ST-2026-004',type:'食堂',unit:'采油二厂',address:'甘肃省庆阳市庆城县采油二厂驻地',leader:'刘文静',phone:'136****4004',terminal:'是',
|
||||
bizHours:[{meal:'早餐',weekdays:[1,1,1,1,1,0,0],start:'07:00',end:'09:00'},{meal:'午餐',weekdays:[1,1,1,1,1,0,0],start:'12:00',end:'14:00'},{meal:'晚餐',weekdays:[1,1,1,1,1,0,0],start:'18:00',end:'20:00'}],
|
||||
images:{logo:true,storefront:true,environment:true},
|
||||
qual:{bizLicense:{code:'91621021MA6TXAAAAA',validFrom:'2024-01-01',validTo:'2034-01-01'},foodPermit:{code:'JY16106666777',validFrom:'2024-02-01',validTo:'2029-02-01'},idCard:{front:true,back:false}}
|
||||
},
|
||||
{name:'采气一厂营养餐厅',code:'ST-2026-005',type:'餐厅',unit:'采气一厂',address:'陕西省榆林市靖边县采气一厂作业区',leader:'陈伟',phone:'135****5005',terminal:'否',
|
||||
bizHours:[{meal:'午餐',weekdays:[1,1,1,1,1,1,0],start:'11:30',end:'13:30'},{meal:'晚餐',weekdays:[1,1,1,1,1,1,1],start:'17:30',end:'20:00'}],
|
||||
images:{logo:true,storefront:false,environment:true},
|
||||
qual:{bizLicense:{code:'91610824MA6TXBBBBB',validFrom:'2023-09-01',validTo:'2033-09-01'},foodPermit:{code:'JY16107777888',validFrom:'2023-10-01',validTo:'2028-10-01'},idCard:{front:true,back:true}}
|
||||
}
|
||||
];
|
||||
|
||||
function openDrawer(idx){
|
||||
var d=canteens[idx];
|
||||
var tagClass=d.type==='食堂'?'tag-blue':'tag-green';
|
||||
var terminalTag=d.terminal==='是'?'tag-green':'tag-gray';
|
||||
|
||||
/* 构建营业时段 HTML */
|
||||
var bizHtml='';
|
||||
d.bizHours.forEach(function(bh){
|
||||
var wdLabels=['一','二','三','四','五','六','日'];
|
||||
var wdHtml='';
|
||||
bh.weekdays.forEach(function(active,i){
|
||||
wdHtml+='<span class="biz-wd'+(active?' active':'')+'">'+wdLabels[i]+'</span>';
|
||||
});
|
||||
bizHtml+='<div class="detail-biz-item">'+
|
||||
'<span class="biz-meal">'+bh.meal+'</span>'+
|
||||
'<span class="biz-weekdays">'+wdHtml+'</span>'+
|
||||
'<span class="biz-time">'+bh.start+' - '+bh.end+'</span>'+
|
||||
'</div>';
|
||||
});
|
||||
|
||||
/* 构建场所图片 HTML */
|
||||
var imgLabels={logo:'场所Logo',storefront:'门头照',environment:'环境照'};
|
||||
var imgIcons={logo:'🏢',storefront:'📷',environment:'🖼️'};
|
||||
var imgHtml='<div class="detail-img-row">';
|
||||
['logo','storefront','environment'].forEach(function(key){
|
||||
var hasImg=d.images[key];
|
||||
imgHtml+='<div class="detail-img-item">'+
|
||||
'<div class="thumb" style="'+(hasImg?'background:#E6F7FF;border-color:#91D5FF;':'')+'">'+
|
||||
(hasImg?'📷':'—')+'</div>'+
|
||||
'<div class="thumb-label">'+imgLabels[key]+(hasImg?' ✅':'')+'</div>'+
|
||||
'</div>';
|
||||
});
|
||||
imgHtml+='</div>';
|
||||
|
||||
/* 构建资质信息 HTML */
|
||||
var qualHtml='';
|
||||
/* 营业执照 */
|
||||
qualHtml+='<div class="detail-qual-card">'+
|
||||
'<div class="detail-qual-card__title">📄 营业执照</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">照片:</span>已上传</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">信用代码:</span>'+d.qual.bizLicense.code+'</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">有效期:</span>'+d.qual.bizLicense.validFrom+' 至 '+d.qual.bizLicense.validTo+'</div>'+
|
||||
'</div>';
|
||||
/* 食品经营许可证 */
|
||||
qualHtml+='<div class="detail-qual-card">'+
|
||||
'<div class="detail-qual-card__title">📋 食品经营许可证</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">照片:</span>已上传</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">许可证编号:</span>'+d.qual.foodPermit.code+'</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">有效期:</span>'+d.qual.foodPermit.validFrom+' 至 '+d.qual.foodPermit.validTo+'</div>'+
|
||||
'</div>';
|
||||
/* 法人身份证 */
|
||||
var idFront=d.qual.idCard.front?'已上传':'未上传';
|
||||
var idBack=d.qual.idCard.back?'已上传':'未上传';
|
||||
qualHtml+='<div class="detail-qual-card">'+
|
||||
'<div class="detail-qual-card__title">🪪 法人身份证<span style="font-weight:400;color:var(--text-placeholder);">(非必填)</span></div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">正面(人像面):</span>'+idFront+'</div>'+
|
||||
'<div class="detail-qual-card__row"><span class="detail-qual-card__label">反面(国徽面):</span>'+idBack+'</div>'+
|
||||
'</div>';
|
||||
|
||||
document.getElementById('drawerBody').innerHTML=
|
||||
/* 基本信息 */
|
||||
'<div class="detail-section">'+
|
||||
'<div class="detail-section__title"><span class="dot"></span>基本信息</div>'+
|
||||
'<div class="detail-row"><div class="detail-label">场所编号</div><div class="detail-value">'+d.code+'</div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">场所名称</div><div class="detail-value">'+d.name+'</div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">场所类型</div><div class="detail-value"><span class="tag '+(d.type==='食堂'?'tag-blue':'tag-green')+'">'+d.type+'</span></div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">场所类型</div><div class="detail-value"><span class="tag '+tagClass+'">'+d.type+'</span></div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">所属单位</div><div class="detail-value">'+d.unit+'</div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">详细地址</div><div class="detail-value">'+d.address+'</div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">负责人</div><div class="detail-value">'+d.leader+'</div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">负责人电话</div><div class="detail-value">'+d.phone+'</div></div>'+
|
||||
'<div class="detail-row"><div class="detail-label">使用三减终端</div><div class="detail-value"><span class="tag '+(d.terminal==='是'?'tag-green':'tag-gray')+'">'+d.terminal+'</span></div></div>';
|
||||
'<div class="detail-row"><div class="detail-label">使用三减终端</div><div class="detail-value"><span class="tag '+terminalTag+'">'+d.terminal+'</span></div></div>'+
|
||||
'</div>'+
|
||||
/* 营业时段 */
|
||||
'<div class="detail-section">'+
|
||||
'<div class="detail-section__title"><span class="dot"></span>营业时段</div>'+
|
||||
bizHtml+
|
||||
'</div>'+
|
||||
/* 场所图片 */
|
||||
'<div class="detail-section">'+
|
||||
'<div class="detail-section__title"><span class="dot"></span>场所图片</div>'+
|
||||
imgHtml+
|
||||
'</div>'+
|
||||
/* 资质信息 */
|
||||
'<div class="detail-section">'+
|
||||
'<div class="detail-section__title"><span class="dot"></span>资质信息</div>'+
|
||||
qualHtml+
|
||||
'</div>';
|
||||
|
||||
document.getElementById('drawerMask').classList.add('show');
|
||||
document.getElementById('drawer').classList.add('show');
|
||||
}
|
||||
|
||||
@@ -153,6 +153,26 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
thead th:last-child,tbody td.td-actions{position:sticky;right:0;background:#fff;box-shadow:-4px 0 8px rgba(0,0,0,0.04);z-index:2;white-space:nowrap;}
|
||||
thead th:last-child{background:#FAFAFA;}
|
||||
tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
|
||||
/* 分类管理表格 */
|
||||
.cat-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.cat-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
.cat-table td{padding:8px 12px;border-bottom:1px solid #F5F5F5;color:var(--text-primary);}
|
||||
.cat-table tbody tr:hover{background:#F8FBFF;}
|
||||
.cat-toggle{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:1px solid var(--border);border-radius:3px;cursor:pointer;margin-right:6px;font-size:12px;line-height:1;background:#fff;color:var(--text-secondary);flex-shrink:0;vertical-align:middle;}
|
||||
.cat-toggle:hover{border-color:var(--primary);color:var(--primary);}
|
||||
.cat-toggle.leaf{visibility:hidden;}
|
||||
.cat-table td.td-actions{white-space:nowrap;}
|
||||
.cat-table td.td-actions .link{margin:0 4px;}
|
||||
|
||||
/* 状态胶囊开关 */
|
||||
.status-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
|
||||
.status-toggle__switch{width:36px;height:20px;background:#D9D9D9;border-radius:10px;position:relative;transition:background .2s;flex-shrink:0;}
|
||||
.status-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,0.15);}
|
||||
.status-toggle.active .status-toggle__switch{background:var(--success);}
|
||||
.status-toggle.active .status-toggle__switch::after{transform:translateX(16px);}
|
||||
.status-toggle__label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.status-toggle.active .status-toggle__label{color:var(--success);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -246,6 +266,7 @@ tbody tr:hover td.td-actions{background:#F8FBFF;}
|
||||
<div class="panel-left__actions">
|
||||
<a href="din-dish-add.html" class="btn btn-primary">➕ 新增餐品</a>
|
||||
<button class="btn btn-default">📤 批量导入</button>
|
||||
<button class="btn btn-default" onclick="openDishCategoryDialog()">📂 餐品分类管理</button>
|
||||
</div>
|
||||
<div class="food-list" id="dishList"></div>
|
||||
<div class="panel-left__footer">
|
||||
@@ -510,6 +531,123 @@ function toggleCleanSale(el){
|
||||
// 初始化
|
||||
renderDishList();
|
||||
renderDetail();
|
||||
|
||||
/* ========== 餐品分类管理 ========== */
|
||||
var nextDishCatId = 100;
|
||||
var dishCategories = [
|
||||
{id:1, name:'主食', code:'01', level:1, sort:1, status:'启用', parentId:null},
|
||||
{id:2, name:'荤菜', code:'02', level:1, sort:2, status:'启用', parentId:null},
|
||||
{id:3, name:'素菜', code:'03', level:1, sort:3, status:'启用', parentId:null},
|
||||
{id:4, name:'汤品', code:'04', level:1, sort:4, status:'启用', parentId:null},
|
||||
{id:5, name:'小吃', code:'05', level:1, sort:5, status:'启用', parentId:null},
|
||||
{id:6, name:'饮品', code:'06', level:1, sort:6, status:'启用', parentId:null},
|
||||
{id:7, name:'甜品', code:'07', level:1, sort:7, status:'启用', parentId:null},
|
||||
{id:8, name:'套餐', code:'08', level:1, sort:8, status:'禁用', parentId:null}
|
||||
];
|
||||
var expandedDishCats = {};
|
||||
var editingDishCatId = null;
|
||||
var editingDishParentId = null;
|
||||
var MAX_DISH_LEVEL = 2;
|
||||
|
||||
function openDishCategoryDialog(){
|
||||
document.getElementById('dishCategoryDialog').classList.add('open');
|
||||
renderDishCatTable();
|
||||
}
|
||||
function closeDishCategoryDialog(){
|
||||
document.getElementById('dishCategoryDialog').classList.remove('open');
|
||||
}
|
||||
function toggleDishCatStatus(id){
|
||||
var cat = dishCategories.find(function(c){return c.id===id;});
|
||||
if(!cat) return;
|
||||
cat.status = cat.status==='启用'?'禁用':'启用';
|
||||
renderDishCatTable();
|
||||
}
|
||||
function toggleDishCatExpand(id){
|
||||
expandedDishCats[id] = !expandedDishCats[id];
|
||||
renderDishCatTable();
|
||||
}
|
||||
function getDishCatChildren(parentId){
|
||||
return dishCategories.filter(function(c){return c.parentId===parentId;}).sort(function(a,b){return a.sort-b.sort;});
|
||||
}
|
||||
function renderDishCatTable(){
|
||||
var tops = dishCategories.filter(function(c){return c.parentId===null;}).sort(function(a,b){return a.sort-b.sort;});
|
||||
var html = '';
|
||||
tops.forEach(function(cat){
|
||||
html += renderDishCatRow(cat);
|
||||
if(expandedDishCats[cat.id]){
|
||||
getDishCatChildren(cat.id).forEach(function(child){
|
||||
html += renderDishCatRow(child, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
document.getElementById('dishCatTableBody').innerHTML = html || '<tr><td colspan="6" style="text-align:center;color:var(--text-placeholder);padding:40px;">暂无分类数据</td></tr>';
|
||||
}
|
||||
function renderDishCatRow(cat, isChild){
|
||||
var hasChildren = getDishCatChildren(cat.id).length > 0;
|
||||
var isExpanded = expandedDishCats[cat.id];
|
||||
var toggleHtml = hasChildren
|
||||
? '<span class="cat-toggle" onclick="event.stopPropagation();toggleDishCatExpand('+cat.id+')">'+(isExpanded?'−':'+')+'</span>'
|
||||
: '<span class="cat-toggle leaf">+</span>';
|
||||
var rowClass = isChild ? 'cat-row-l2' : '';
|
||||
var activeClass = cat.status==='启用'?' active':'';
|
||||
var statusLabel = cat.status==='启用'?'启用':'禁用';
|
||||
var statusHtml = '<div class="status-toggle'+activeClass+'" onclick="event.stopPropagation();toggleDishCatStatus('+cat.id+')"><span class="status-toggle__switch"></span><span class="status-toggle__label">'+statusLabel+'</span></div>';
|
||||
var canAddChild = cat.level < MAX_DISH_LEVEL;
|
||||
var addChildBtn = canAddChild ? '<span class="link" onclick="openAddDishCat('+cat.id+')">新增下级</span>' : '';
|
||||
return '<tr class="'+rowClass+'"><td class="cat-name">'+toggleHtml+cat.name+'</td><td>'+cat.code+'</td><td>'+cat.level+'</td><td>'+cat.sort+'</td><td>'+statusHtml+'</td><td class="td-actions"><span class="link" onclick="openEditDishCat('+cat.id+')">编辑</span><span class="link" style="color:var(--danger);" onclick="deleteDishCat('+cat.id+')">删除</span>'+addChildBtn+'</td></tr>';
|
||||
}
|
||||
function openAddDishCat(parentId){
|
||||
editingDishCatId = null;
|
||||
editingDishParentId = parentId || null;
|
||||
document.getElementById('dishCatFormDialogTitle').textContent = parentId ? '新增下级分类' : '新增分类';
|
||||
document.getElementById('dishCatFormName').value = '';
|
||||
document.getElementById('dishCatFormCode').value = '';
|
||||
document.getElementById('dishCatFormSort').value = '1';
|
||||
document.getElementById('dishCatFormStatus').value = '启用';
|
||||
document.getElementById('dishCatFormDialog').classList.add('open');
|
||||
}
|
||||
function openEditDishCat(id){
|
||||
var cat = dishCategories.find(function(c){return c.id===id;});
|
||||
if(!cat) return;
|
||||
editingDishCatId = id;
|
||||
editingDishParentId = cat.parentId;
|
||||
document.getElementById('dishCatFormDialogTitle').textContent = '编辑分类';
|
||||
document.getElementById('dishCatFormName').value = cat.name;
|
||||
document.getElementById('dishCatFormCode').value = cat.code;
|
||||
document.getElementById('dishCatFormSort').value = cat.sort;
|
||||
document.getElementById('dishCatFormStatus').value = cat.status;
|
||||
document.getElementById('dishCatFormDialog').classList.add('open');
|
||||
}
|
||||
function closeDishCatFormDialog(){
|
||||
document.getElementById('dishCatFormDialog').classList.remove('open');
|
||||
editingDishCatId = null;
|
||||
editingDishParentId = null;
|
||||
}
|
||||
function saveDishCatForm(){
|
||||
var name = document.getElementById('dishCatFormName').value.trim();
|
||||
var code = document.getElementById('dishCatFormCode').value.trim();
|
||||
var sort = parseInt(document.getElementById('dishCatFormSort').value) || 1;
|
||||
var status = document.getElementById('dishCatFormStatus').value;
|
||||
if(!name||!code){alert('请填写分类名称和分类编码');return;}
|
||||
if(editingDishCatId){
|
||||
var cat = dishCategories.find(function(c){return c.id===editingDishCatId;});
|
||||
if(cat){cat.name=name;cat.code=code;cat.sort=sort;cat.status=status;}
|
||||
} else {
|
||||
var level = editingDishParentId ? 2 : 1;
|
||||
dishCategories.push({id:nextDishCatId++,name:name,code:code,level:level,sort:sort,status:status,parentId:editingDishParentId});
|
||||
if(editingDishParentId) expandedDishCats[editingDishParentId] = true;
|
||||
}
|
||||
closeDishCatFormDialog();
|
||||
renderDishCatTable();
|
||||
}
|
||||
function deleteDishCat(id){
|
||||
var cat = dishCategories.find(function(c){return c.id===id;});
|
||||
if(!cat) return;
|
||||
if(getDishCatChildren(id).length > 0){alert('该分类下存在子级分类,请先删除子级分类后再删除。');return;}
|
||||
if(!confirm('确定要删除分类「'+cat.name+'」吗?删除后不可恢复。')) return;
|
||||
dishCategories = dishCategories.filter(function(c){return c.id!==id;});
|
||||
renderDishCatTable();
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 新增食材弹窗 -->
|
||||
@@ -547,5 +685,51 @@ renderDetail();
|
||||
function openIngredientDialog(){document.getElementById('ingredientDialog').classList.add('open');}
|
||||
function closeIngredientDialog(){document.getElementById('ingredientDialog').classList.remove('open');}
|
||||
</script>
|
||||
<!-- 餐品分类管理弹窗 -->
|
||||
<div class="dialog-mask" id="dishCategoryDialog">
|
||||
<div class="dialog" style="width:820px;">
|
||||
<div class="dialog__header">
|
||||
<div class="dialog__title">餐品分类管理</div>
|
||||
<button class="dialog__close" onclick="closeDishCategoryDialog()">✕</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<!-- 工具栏 -->
|
||||
<div style="margin-bottom:12px;">
|
||||
<button class="btn btn-primary" onclick="openAddDishCat()" style="height:30px;font-size:var(--font-xs);">+ 新增一级分类</button>
|
||||
</div>
|
||||
<!-- 分类表格 -->
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="cat-table">
|
||||
<thead><tr><th style="width:180px;">分类名称</th><th style="width:100px;">分类编码</th><th style="width:60px;">层级</th><th style="width:60px;">排序</th><th style="width:80px;">状态</th><th style="width:180px;">操作</th></tr></thead>
|
||||
<tbody id="dishCatTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog__footer">
|
||||
<button class="btn btn-default" onclick="closeDishCategoryDialog()">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 餐品分类表单子弹窗 -->
|
||||
<div class="dialog-mask" id="dishCatFormDialog">
|
||||
<div class="dialog" style="width:480px;">
|
||||
<div class="dialog__header">
|
||||
<div class="dialog__title" id="dishCatFormDialogTitle">新增分类</div>
|
||||
<button class="dialog__close" onclick="closeDishCatFormDialog()">✕</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<div class="form-group"><label>分类名称<span class="required">*</span></label><input class="form-input" id="dishCatFormName" placeholder="请输入分类名称"></div>
|
||||
<div class="form-row">
|
||||
<div class="form-group"><label>分类编码<span class="required">*</span></label><input class="form-input" id="dishCatFormCode" placeholder="请输入分类编码"></div>
|
||||
<div class="form-group"><label>排序</label><input class="form-input" id="dishCatFormSort" type="number" value="1"></div>
|
||||
</div>
|
||||
<div class="form-group"><label>状态</label><select class="form-select" id="dishCatFormStatus"><option>启用</option><option>禁用</option></select></div>
|
||||
</div>
|
||||
<div class="dialog__footer">
|
||||
<button class="btn btn-default" onclick="closeDishCatFormDialog()">取消</button>
|
||||
<button class="btn btn-primary" onclick="saveDishCatForm()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -111,6 +111,29 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.dynamic-row__del:hover{color:var(--danger);background:#FFF1F0;}
|
||||
.dynamic-row-add{display:inline-flex;align-items:center;gap:4px;padding:6px 12px;border:1px dashed var(--primary);color:var(--primary);border-radius:var(--radius-sm);font-size:var(--font-xs);cursor:pointer;background:none;margin-top:8px;transition:all .2s;}
|
||||
.dynamic-row-add:hover{background:var(--primary-light);}
|
||||
/* 复制食材弹窗 */
|
||||
.copy-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);z-index:300;align-items:center;justify-content:center;}
|
||||
.copy-overlay.show{display:flex;}
|
||||
.copy-dialog{background:#fff;border-radius:var(--radius-lg);width:560px;max-width:90vw;box-shadow:var(--shadow-md);display:flex;flex-direction:column;max-height:80vh;}
|
||||
.copy-dialog__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);flex-shrink:0;}
|
||||
.copy-dialog__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.copy-dialog__close{width:32px;height:32px;border:none;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);color:var(--text-placeholder);font-size:18px;}
|
||||
.copy-dialog__close:hover{background:#F5F5F5;color:var(--text-primary);}
|
||||
.copy-dialog__search{padding:12px 24px;border-bottom:1px solid #F0F0F0;flex-shrink:0;}
|
||||
.copy-dialog__search input{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;}
|
||||
.copy-dialog__search input:focus{border-color:var(--primary);}
|
||||
.copy-dialog__body{flex:1;overflow-y:auto;padding:0;}
|
||||
.copy-dialog__empty{padding:40px 24px;text-align:center;color:var(--text-placeholder);font-size:var(--font-sm);}
|
||||
.copy-item{display:flex;align-items:center;gap:12px;padding:12px 24px;cursor:pointer;transition:background .15s;border-bottom:1px solid #F5F5F5;}
|
||||
.copy-item:hover{background:#F8FBFF;}
|
||||
.copy-item.active{background:var(--primary-light);}
|
||||
.copy-item__icon{width:36px;height:36px;border-radius:var(--radius-sm);background:#F5F7FA;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;}
|
||||
.copy-item__info{flex:1;min-width:0;}
|
||||
.copy-item__name{font-size:var(--font-sm);font-weight:500;color:var(--text-primary);}
|
||||
.copy-item__meta{font-size:var(--font-xs);color:var(--text-placeholder);margin-top:2px;display:flex;gap:8px;}
|
||||
.copy-item__check{display:none;width:20px;height:20px;border-radius:50%;background:var(--primary);color:#fff;font-size:12px;align-items:center;justify-content:center;flex-shrink:0;}
|
||||
.copy-item.active .copy-item__check{display:flex;}
|
||||
.copy-dialog__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);flex-shrink:0;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -208,6 +231,26 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 复制食材弹窗 -->
|
||||
<div class="copy-overlay" id="copyOverlay" onclick="if(event.target===this)closeCopyDialog()">
|
||||
<div class="copy-dialog">
|
||||
<div class="copy-dialog__header">
|
||||
<div class="copy-dialog__title">选择要复制的食材</div>
|
||||
<button class="copy-dialog__close" onclick="closeCopyDialog()">×</button>
|
||||
</div>
|
||||
<div class="copy-dialog__search">
|
||||
<input type="text" id="copySearchInput" placeholder="搜索食物名称、编码或分类..." oninput="filterCopyList()">
|
||||
</div>
|
||||
<div class="copy-dialog__body" id="copyListBody">
|
||||
<!-- JS 动态渲染 -->
|
||||
</div>
|
||||
<div class="copy-dialog__footer">
|
||||
<button class="btn btn-default" onclick="closeCopyDialog()">取消</button>
|
||||
<button class="btn btn-primary" id="btnConfirmCopy" disabled onclick="confirmCopy()">确认复制</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
|
||||
|
||||
@@ -216,7 +259,7 @@ var totalSteps = 5;
|
||||
|
||||
var STEP_HTML = [
|
||||
// Step 0: 基本信息
|
||||
`<div class="form-card__title">📋 基本信息</div>
|
||||
`<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;"><div class="form-card__title" style="margin-bottom:0;padding-bottom:0;border-bottom:none;flex-shrink:0;">📋 基本信息</div><button type="button" class="btn btn-default" onclick="openCopyDialog()" style="height:32px;font-size:var(--font-xs);">📋 从已有食材复制</button></div>
|
||||
<div class="form-grid">
|
||||
<div class="form-group"><label class="form-label">食物编码<span class="required">*</span></label><input class="form-input" id="foodCode" placeholder="系统自动生成(分类+自编码)" readonly style="background:#f5f5f5;cursor:not-allowed;"></div>
|
||||
<div class="form-group"><label class="form-label">食物名称<span class="required">*</span></label><input class="form-input" id="foodName" placeholder="请输入食物名称"></div>
|
||||
@@ -745,6 +788,181 @@ function initEditMode(){
|
||||
}
|
||||
}
|
||||
|
||||
/* === 复制已有食材 === */
|
||||
var copySelectedCode = '';
|
||||
var copyFoodList = [];
|
||||
|
||||
/* 构建可选食材列表(从 EDIT_FOODS 提取为数组,排除当前编辑的食材本身) */
|
||||
function buildCopyFoodList(){
|
||||
copyFoodList = [];
|
||||
var isEdit = (pageMode === 'edit' && editCode);
|
||||
for(var code in EDIT_FOODS){
|
||||
// 编辑模式下排除自身
|
||||
if(isEdit && code === editCode) continue;
|
||||
var f = EDIT_FOODS[code];
|
||||
copyFoodList.push({
|
||||
code: f.code,
|
||||
name: f.name,
|
||||
firstClass: f.firstClass,
|
||||
secondClass: f.secondClass,
|
||||
calorie: f.nutri ? f.nutri.calorie : 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* 渲染弹窗食材列表 */
|
||||
function renderCopyList(filterText){
|
||||
var body = document.getElementById('copyListBody');
|
||||
if(!body) return;
|
||||
var kw = (filterText || '').toLowerCase();
|
||||
var filtered = copyFoodList;
|
||||
if(kw){
|
||||
filtered = copyFoodList.filter(function(item){
|
||||
return item.name.toLowerCase().indexOf(kw) !== -1
|
||||
|| item.code.toLowerCase().indexOf(kw) !== -1
|
||||
|| item.firstClass.toLowerCase().indexOf(kw) !== -1
|
||||
|| item.secondClass.toLowerCase().indexOf(kw) !== -1;
|
||||
});
|
||||
}
|
||||
if(filtered.length === 0){
|
||||
body.innerHTML = '<div class="copy-dialog__empty">未找到匹配的食材</div>';
|
||||
return;
|
||||
}
|
||||
var html = '';
|
||||
filtered.forEach(function(item){
|
||||
var activeClass = (copySelectedCode === item.code) ? ' active' : '';
|
||||
html += '<div class="copy-item' + activeClass + '" onclick="selectCopyItem(\'' + item.code + '\')">';
|
||||
html += '<div class="copy-item__icon">🧪</div>';
|
||||
html += '<div class="copy-item__info"><div class="copy-item__name">' + item.name + '</div>';
|
||||
html += '<div class="copy-item__meta"><span>' + item.code + '</span><span>' + item.firstClass + ' / ' + item.secondClass + '</span><span>' + item.calorie + ' kcal</span></div></div>';
|
||||
html += '<div class="copy-item__check">✓</div>';
|
||||
html += '</div>';
|
||||
});
|
||||
body.innerHTML = html;
|
||||
}
|
||||
|
||||
/* 搜索过滤 */
|
||||
function filterCopyList(){
|
||||
var input = document.getElementById('copySearchInput');
|
||||
var kw = input ? input.value : '';
|
||||
copySelectedCode = '';
|
||||
updateCopyConfirmBtn();
|
||||
renderCopyList(kw);
|
||||
}
|
||||
|
||||
/* 选中食材 */
|
||||
function selectCopyItem(code){
|
||||
copySelectedCode = code;
|
||||
renderCopyList(document.getElementById('copySearchInput').value);
|
||||
updateCopyConfirmBtn();
|
||||
}
|
||||
|
||||
/* 更新确认按钮状态 */
|
||||
function updateCopyConfirmBtn(){
|
||||
var btn = document.getElementById('btnConfirmCopy');
|
||||
if(btn) btn.disabled = !copySelectedCode;
|
||||
}
|
||||
|
||||
/* 打开弹窗 */
|
||||
function openCopyDialog(){
|
||||
buildCopyFoodList();
|
||||
copySelectedCode = '';
|
||||
var overlay = document.getElementById('copyOverlay');
|
||||
var input = document.getElementById('copySearchInput');
|
||||
if(input) input.value = '';
|
||||
if(overlay) overlay.classList.add('show');
|
||||
renderCopyList('');
|
||||
updateCopyConfirmBtn();
|
||||
}
|
||||
|
||||
/* 关闭弹窗 */
|
||||
function closeCopyDialog(){
|
||||
var overlay = document.getElementById('copyOverlay');
|
||||
if(overlay) overlay.classList.remove('show');
|
||||
}
|
||||
|
||||
/* 确认复制:填充 formData(排除食物名称、食物分类、别名、食材图片) */
|
||||
function confirmCopy(){
|
||||
if(!copySelectedCode) return;
|
||||
var food = EDIT_FOODS[copySelectedCode];
|
||||
if(!food) return;
|
||||
|
||||
// 保存当前食物名称、别名、分类和图片,复制后还原
|
||||
var savedName = formData['foodName'] || '';
|
||||
var savedAlias = formData['foodAlias'] || '';
|
||||
var savedCatL1 = selectedCategory.l1;
|
||||
var savedCatL2 = selectedCategory.l2;
|
||||
var savedImage = imageData;
|
||||
|
||||
// 基本信息(排除名称、别名、分类、图片)
|
||||
formData['edible'] = food.edible || '';
|
||||
formData['waterRate'] = food.waterRate || '';
|
||||
formData['dataSource'] = food.dataSource === 0 ? '0' : String(food.dataSource || '0');
|
||||
|
||||
// 净菜类型
|
||||
vegTypes = food.vegTypes ? food.vegTypes.slice() : [];
|
||||
|
||||
// 适配人群
|
||||
crowdList = food.crowd ? food.crowd.map(function(c){ return {name:c.name, level:c.level}; }) : [];
|
||||
|
||||
// 营养成分
|
||||
if(food.nutri){
|
||||
var n = food.nutri;
|
||||
for(var key in n){
|
||||
if(key === 'mg'){
|
||||
formData['mg_el'] = String(n[key]);
|
||||
} else {
|
||||
formData[key] = String(n[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 味道档案
|
||||
if(food.taste){
|
||||
formData['tasteMain'] = food.taste.main || '';
|
||||
formData['tasteSub'] = food.taste.sub || '';
|
||||
formData['tasteIntensity'] = food.taste.intensity || '';
|
||||
formData['tasteDesc'] = food.taste.desc || '';
|
||||
}
|
||||
|
||||
// 口感档案
|
||||
if(food.texture){
|
||||
formData['textureMain'] = food.texture.main || '';
|
||||
formData['chewDiff'] = food.texture.chew || '';
|
||||
formData['digestDiff'] = food.texture.digest || '';
|
||||
formData['textureDesc'] = food.texture.desc || '';
|
||||
formData['cookAdvice'] = food.texture.cook || '';
|
||||
}
|
||||
|
||||
// 色泽档案
|
||||
if(food.color){
|
||||
formData['colorMain'] = food.color.main || '';
|
||||
formData['colorGrade'] = food.color.grade || '';
|
||||
formData['colorDesc'] = food.color.desc || '';
|
||||
formData['colorCause'] = food.color.cause || '';
|
||||
}
|
||||
|
||||
// 还原名称、别名、分类、图片
|
||||
formData['foodName'] = savedName;
|
||||
formData['foodAlias'] = savedAlias;
|
||||
selectedCategory.l1 = savedCatL1;
|
||||
selectedCategory.l2 = savedCatL2;
|
||||
imageData = savedImage;
|
||||
|
||||
// 刷新当前步骤的显示
|
||||
renderStep();
|
||||
// 刷新级联选择器回显
|
||||
if(selectedCategory.l1){
|
||||
var trig = document.getElementById('addCascadeTrigger');
|
||||
if(trig){
|
||||
trig.textContent = selectedCategory.l1 + (selectedCategory.l2 ? ' / ' + selectedCategory.l2 : '');
|
||||
trig.classList.add('has-value');
|
||||
}
|
||||
}
|
||||
|
||||
closeCopyDialog();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
initEditMode();
|
||||
renderStep();
|
||||
|
||||
@@ -142,6 +142,41 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
.food-item__source{font-size:10px;padding:1px 6px;border-radius:8px;flex-shrink:0;white-space:nowrap;}
|
||||
.food-item__source--sys{background:var(--primary-light);color:var(--primary);}
|
||||
.food-item__source--manual{background:#FFF7E6;color:var(--warning);}
|
||||
|
||||
/* 弹窗 */
|
||||
.dialog-mask{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.45);z-index:200;align-items:center;justify-content:center;}
|
||||
.dialog-mask.open{display:flex;}
|
||||
.dialog{background:#fff;border-radius:var(--radius-lg);box-shadow:var(--shadow-md);max-height:80vh;display:flex;flex-direction:column;}
|
||||
.dialog__header{display:flex;align-items:center;justify-content:space-between;padding:20px 24px 16px;border-bottom:1px solid var(--border);flex-shrink:0;}
|
||||
.dialog__title{font-size:var(--font-lg);font-weight:600;}
|
||||
.dialog__close{width:32px;height:32px;border:none;background:none;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:var(--radius-sm);color:var(--text-placeholder);font-size:16px;}
|
||||
.dialog__close:hover{background:#F5F5F5;color:var(--text-primary);}
|
||||
.dialog__body{flex:1;overflow-y:auto;padding:16px 24px;}
|
||||
.dialog__footer{display:flex;justify-content:flex-end;gap:8px;padding:16px 24px;border-top:1px solid var(--border);flex-shrink:0;}
|
||||
.form-input,.form-select{width:100%;height:36px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0 12px;font-size:var(--font-sm);outline:none;background:#fff;}
|
||||
.form-input:focus,.form-select:focus{border-color:var(--primary);}
|
||||
.form-select{appearance:none;background:#fff url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23999'/%3E%3C/svg%3E") no-repeat right 10px center;padding-right:28px;cursor:pointer;}
|
||||
|
||||
/* 分类管理表格 */
|
||||
.cat-table{width:100%;border-collapse:collapse;font-size:var(--font-sm);}
|
||||
.cat-table th{background:#FAFAFA;padding:10px 12px;text-align:left;color:var(--text-secondary);font-weight:500;border-bottom:1px solid var(--border);white-space:nowrap;}
|
||||
.cat-table td{padding:8px 12px;border-bottom:1px solid #F5F5F5;color:var(--text-primary);}
|
||||
.cat-table tbody tr:hover{background:#F8FBFF;}
|
||||
.cat-toggle{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;border:1px solid var(--border);border-radius:3px;cursor:pointer;margin-right:6px;font-size:12px;line-height:1;background:#fff;color:var(--text-secondary);flex-shrink:0;vertical-align:middle;}
|
||||
.cat-toggle:hover{border-color:var(--primary);color:var(--primary);}
|
||||
.cat-toggle.leaf{visibility:hidden;}
|
||||
.cat-row-l2 .cat-name{padding-left:28px!important;}
|
||||
.cat-table td.td-actions{white-space:nowrap;}
|
||||
.cat-table td.td-actions .link{margin:0 4px;}
|
||||
|
||||
/* 状态胶囊开关 */
|
||||
.status-toggle{display:inline-flex;align-items:center;gap:6px;cursor:pointer;user-select:none;}
|
||||
.status-toggle__switch{width:36px;height:20px;background:#D9D9D9;border-radius:10px;position:relative;transition:background .2s;flex-shrink:0;}
|
||||
.status-toggle__switch::after{content:'';position:absolute;left:2px;top:2px;width:16px;height:16px;background:#fff;border-radius:50%;transition:transform .2s;box-shadow:0 1px 3px rgba(0,0,0,0.15);}
|
||||
.status-toggle.active .status-toggle__switch{background:var(--success);}
|
||||
.status-toggle.active .status-toggle__switch::after{transform:translateX(16px);}
|
||||
.status-toggle__label{font-size:var(--font-xs);color:var(--text-placeholder);}
|
||||
.status-toggle.active .status-toggle__label{color:var(--success);}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -240,7 +275,7 @@ body{font-family:-apple-system,BlinkMacSystemFont,'PingFang SC','Microsoft YaHei
|
||||
</div>
|
||||
<div class="panel-left__actions">
|
||||
<a href="din-ingredient-add.html" class="btn btn-primary">➕ 新增食物</a>
|
||||
<a class="btn btn-primary">🖥 分类管理</a>
|
||||
<button class="btn btn-default" onclick="openCategoryDialog()">📂 食材分类管理</button>
|
||||
</div>
|
||||
<div class="food-list">
|
||||
<div class="food-item active" onclick="selectFood(0)">
|
||||
@@ -585,6 +620,196 @@ renderDetail();
|
||||
|
||||
// 侧边栏子组展开/收起
|
||||
function toggleSubgroup(header){header.parentElement.classList.toggle('open');}
|
||||
|
||||
/* ========== 食材分类管理 ========== */
|
||||
var nextCatId = 100;
|
||||
var ingredientCats = [
|
||||
{id:1, name:'谷薯类', code:'01', level:1, sort:1, status:'启用', parentId:null},
|
||||
{id:2, name:'稻米', code:'0101', level:2, sort:1, status:'启用', parentId:1},
|
||||
{id:3, name:'小麦', code:'0102', level:2, sort:2, status:'启用', parentId:1},
|
||||
{id:4, name:'玉米', code:'0103', level:2, sort:3, status:'启用', parentId:1},
|
||||
{id:5, name:'薯类', code:'0104', level:2, sort:4, status:'启用', parentId:1},
|
||||
{id:6, name:'杂粮', code:'0105', level:2, sort:5, status:'启用', parentId:1},
|
||||
{id:7, name:'畜禽肉类', code:'02', level:1, sort:2, status:'启用', parentId:null},
|
||||
{id:8, name:'猪肉', code:'0201', level:2, sort:1, status:'启用', parentId:7},
|
||||
{id:9, name:'牛肉', code:'0202', level:2, sort:2, status:'启用', parentId:7},
|
||||
{id:10, name:'羊肉', code:'0203', level:2, sort:3, status:'启用', parentId:7},
|
||||
{id:11, name:'鸡肉', code:'0204', level:2, sort:4, status:'启用', parentId:7},
|
||||
{id:12, name:'鸭肉', code:'0205', level:2, sort:5, status:'启用', parentId:7},
|
||||
{id:13, name:'水产类', code:'03', level:1, sort:3, status:'启用', parentId:null},
|
||||
{id:14, name:'淡水鱼', code:'0301', level:2, sort:1, status:'启用', parentId:13},
|
||||
{id:15, name:'海水鱼', code:'0302', level:2, sort:2, status:'启用', parentId:13},
|
||||
{id:16, name:'虾蟹', code:'0303', level:2, sort:3, status:'启用', parentId:13},
|
||||
{id:17, name:'贝类', code:'0304', level:2, sort:4, status:'启用', parentId:13},
|
||||
{id:18, name:'蛋类', code:'04', level:1, sort:4, status:'启用', parentId:null},
|
||||
{id:19, name:'鸡蛋', code:'0401', level:2, sort:1, status:'启用', parentId:18},
|
||||
{id:20, name:'鸭蛋', code:'0402', level:2, sort:2, status:'启用', parentId:18},
|
||||
{id:21, name:'奶及奶制品', code:'05', level:1, sort:5, status:'启用', parentId:null},
|
||||
{id:22, name:'牛奶', code:'0501', level:2, sort:1, status:'启用', parentId:21},
|
||||
{id:23, name:'酸奶', code:'0502', level:2, sort:2, status:'启用', parentId:21},
|
||||
{id:24, name:'豆及豆制品', code:'06', level:1, sort:6, status:'启用', parentId:null},
|
||||
{id:25, name:'大豆', code:'0601', level:2, sort:1, status:'启用', parentId:24},
|
||||
{id:26, name:'豆制品', code:'0602', level:2, sort:2, status:'启用', parentId:24},
|
||||
{id:27, name:'蔬菜类', code:'07', level:1, sort:7, status:'启用', parentId:null},
|
||||
{id:28, name:'叶菜类', code:'0701', level:2, sort:1, status:'启用', parentId:27},
|
||||
{id:29, name:'根茎类', code:'0702', level:2, sort:2, status:'启用', parentId:27},
|
||||
{id:30, name:'瓜果类', code:'0703', level:2, sort:3, status:'启用', parentId:27},
|
||||
{id:31, name:'菌菇类', code:'0704', level:2, sort:4, status:'启用', parentId:27},
|
||||
{id:32, name:'水果类', code:'08', level:1, sort:8, status:'启用', parentId:null},
|
||||
{id:33, name:'坚果种子类', code:'09', level:1, sort:9, status:'启用', parentId:null},
|
||||
{id:34, name:'油脂类', code:'10', level:1, sort:10, status:'启用', parentId:null},
|
||||
{id:35, name:'调味品类', code:'11', level:1, sort:11, status:'启用', parentId:null}
|
||||
];
|
||||
var expandedCats = {};
|
||||
var editingCatId = null;
|
||||
var editingParentId = null;
|
||||
var MAX_INGREDIENT_LEVEL = 2;
|
||||
|
||||
function openCategoryDialog(){
|
||||
document.getElementById('categoryDialog').classList.add('open');
|
||||
renderCatTable();
|
||||
}
|
||||
function closeCategoryDialog(){
|
||||
document.getElementById('categoryDialog').classList.remove('open');
|
||||
}
|
||||
function toggleCatExpand(id){
|
||||
expandedCats[id] = !expandedCats[id];
|
||||
renderCatTable();
|
||||
}
|
||||
function getCatChildren(parentId){
|
||||
return ingredientCats.filter(function(c){return c.parentId===parentId;}).sort(function(a,b){return a.sort-b.sort;});
|
||||
}
|
||||
function renderCatTable(){
|
||||
var tops = ingredientCats.filter(function(c){return c.parentId===null;}).sort(function(a,b){return a.sort-b.sort;});
|
||||
var html = '';
|
||||
tops.forEach(function(cat){
|
||||
html += renderCatRow(cat);
|
||||
if(expandedCats[cat.id]){
|
||||
getCatChildren(cat.id).forEach(function(child){
|
||||
html += renderCatRow(child, true);
|
||||
});
|
||||
}
|
||||
});
|
||||
document.getElementById('catTableBody').innerHTML = html || '<tr><td colspan="6" style="text-align:center;color:var(--text-placeholder);padding:40px;">暂无分类数据</td></tr>';
|
||||
}
|
||||
function renderCatRow(cat, isChild){
|
||||
var hasChildren = getCatChildren(cat.id).length > 0;
|
||||
var isExpanded = expandedCats[cat.id];
|
||||
var toggleHtml = hasChildren
|
||||
? '<span class="cat-toggle" onclick="event.stopPropagation();toggleCatExpand('+cat.id+')">'+(isExpanded?'−':'+')+'</span>'
|
||||
: '<span class="cat-toggle leaf">+</span>';
|
||||
var rowClass = isChild ? 'cat-row-l2' : '';
|
||||
var activeClass = cat.status==='启用'?' active':'';
|
||||
var statusLabel = cat.status==='启用'?'启用':'禁用';
|
||||
var statusHtml = '<div class="status-toggle'+activeClass+'" onclick="event.stopPropagation();toggleCatStatus('+cat.id+')"><span class="status-toggle__switch"></span><span class="status-toggle__label">'+statusLabel+'</span></div>';
|
||||
var canAddChild = cat.level < MAX_INGREDIENT_LEVEL;
|
||||
var addChildBtn = canAddChild ? '<span class="link" onclick="openAddCat('+cat.id+')">新增下级</span>' : '';
|
||||
return '<tr class="'+rowClass+'"><td class="cat-name">'+toggleHtml+cat.name+'</td><td>'+cat.code+'</td><td>'+cat.level+'</td><td>'+cat.sort+'</td><td>'+statusHtml+'</td><td class="td-actions"><span class="link" onclick="openEditCat('+cat.id+')">编辑</span><span class="link" style="color:var(--danger);" onclick="deleteCat('+cat.id+')">删除</span>'+addChildBtn+'</td></tr>';
|
||||
}
|
||||
function toggleCatStatus(id){
|
||||
var cat = ingredientCats.find(function(c){return c.id===id;});
|
||||
if(!cat) return;
|
||||
cat.status = cat.status==='启用'?'禁用':'启用';
|
||||
renderCatTable();
|
||||
}
|
||||
function openAddCat(parentId){
|
||||
editingCatId = null;
|
||||
editingParentId = parentId;
|
||||
document.getElementById('catFormDialogTitle').textContent = parentId ? '新增下级分类' : '新增一级分类';
|
||||
document.getElementById('catFormName').value = '';
|
||||
document.getElementById('catFormCode').value = '';
|
||||
document.getElementById('catFormSort').value = '1';
|
||||
document.getElementById('catFormStatus').value = '启用';
|
||||
document.getElementById('catFormDialog').classList.add('open');
|
||||
}
|
||||
function openEditCat(id){
|
||||
var cat = ingredientCats.find(function(c){return c.id===id;});
|
||||
if(!cat) return;
|
||||
editingCatId = id;
|
||||
editingParentId = cat.parentId;
|
||||
document.getElementById('catFormDialogTitle').textContent = '编辑分类';
|
||||
document.getElementById('catFormName').value = cat.name;
|
||||
document.getElementById('catFormCode').value = cat.code;
|
||||
document.getElementById('catFormSort').value = cat.sort;
|
||||
document.getElementById('catFormStatus').value = cat.status;
|
||||
document.getElementById('catFormDialog').classList.add('open');
|
||||
}
|
||||
function closeCatFormDialog(){
|
||||
document.getElementById('catFormDialog').classList.remove('open');
|
||||
editingCatId = null;
|
||||
editingParentId = null;
|
||||
}
|
||||
function saveCatForm(){
|
||||
var name = document.getElementById('catFormName').value.trim();
|
||||
var code = document.getElementById('catFormCode').value.trim();
|
||||
var sort = parseInt(document.getElementById('catFormSort').value) || 1;
|
||||
var status = document.getElementById('catFormStatus').value;
|
||||
if(!name||!code){alert('请填写分类名称和分类编码');return;}
|
||||
if(editingCatId){
|
||||
var cat = ingredientCats.find(function(c){return c.id===editingCatId;});
|
||||
if(cat){cat.name=name;cat.code=code;cat.sort=sort;cat.status=status;}
|
||||
} else {
|
||||
var level = editingParentId ? 2 : 1;
|
||||
ingredientCats.push({id:nextCatId++,name:name,code:code,level:level,sort:sort,status:status,parentId:editingParentId});
|
||||
if(editingParentId) expandedCats[editingParentId] = true;
|
||||
}
|
||||
closeCatFormDialog();
|
||||
renderCatTable();
|
||||
}
|
||||
function deleteCat(id){
|
||||
var cat = ingredientCats.find(function(c){return c.id===id;});
|
||||
if(!cat) return;
|
||||
if(getCatChildren(id).length > 0){alert('该分类下存在子级分类,请先删除子级分类后再删除。');return;}
|
||||
if(!confirm('确定要删除分类「'+cat.name+'」吗?删除后不可恢复。')) return;
|
||||
ingredientCats = ingredientCats.filter(function(c){return c.id!==id;});
|
||||
renderCatTable();
|
||||
}
|
||||
</script>
|
||||
<!-- 食材分类管理弹窗 -->
|
||||
<div class="dialog-mask" id="categoryDialog">
|
||||
<div class="dialog" style="width:820px;">
|
||||
<div class="dialog__header">
|
||||
<div class="dialog__title">食材分类管理</div>
|
||||
<button class="dialog__close" onclick="closeCategoryDialog()">✕</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<!-- 工具栏 -->
|
||||
<div style="margin-bottom:12px;">
|
||||
<button class="btn btn-primary" onclick="openAddCat(null)" style="height:30px;font-size:var(--font-xs);">+ 新增一级分类</button>
|
||||
</div>
|
||||
<!-- 分类表格 -->
|
||||
<div style="overflow-x:auto;">
|
||||
<table class="cat-table">
|
||||
<thead><tr><th style="width:180px;">分类名称</th><th style="width:100px;">分类编码</th><th style="width:60px;">层级</th><th style="width:60px;">排序</th><th style="width:80px;">状态</th><th style="width:180px;">操作</th></tr></thead>
|
||||
<tbody id="catTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dialog__footer">
|
||||
<button class="btn btn-default" onclick="closeCategoryDialog()">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分类表单子弹窗 -->
|
||||
<div class="dialog-mask" id="catFormDialog">
|
||||
<div class="dialog" style="width:480px;">
|
||||
<div class="dialog__header">
|
||||
<div class="dialog__title" id="catFormDialogTitle">新增分类</div>
|
||||
<button class="dialog__close" onclick="closeCatFormDialog()">✕</button>
|
||||
</div>
|
||||
<div class="dialog__body">
|
||||
<div class="form-group"><label>分类名称<span class="required">*</span></label><input class="form-input" id="catFormName" placeholder="请输入分类名称"></div>
|
||||
<div class="form-row">
|
||||
<div class="form-group"><label>分类编码<span class="required">*</span></label><input class="form-input" id="catFormCode" placeholder="请输入分类编码"></div>
|
||||
<div class="form-group"><label>排序</label><input class="form-input" id="catFormSort" type="number" value="1"></div>
|
||||
</div>
|
||||
<div class="form-group"><label>状态</label><select class="form-select" id="catFormStatus"><option>启用</option><option>禁用</option></select></div>
|
||||
</div>
|
||||
<div class="dialog__footer">
|
||||
<button class="btn btn-default" onclick="closeCatFormDialog()">取消</button>
|
||||
<button class="btn btn-primary" onclick="saveCatForm()">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
# 营养用餐管理端 CHANGELOG
|
||||
|
||||
## [2026-07-23]
|
||||
|
||||
### 后台营养管理
|
||||
|
||||
- **`din-canteen-add.html`**(场所管理 · 新增/编辑场所):页面重构为 5 个卡片区(基本信息 / 地址信息 / 营业时段 / 场所图片 / 资质信息)。新增餐次时段配置(早餐/午餐/晚餐/夜宵,每时段可独立设置营业日及起止时间),新增场所图片上传区(Logo/门头照/环境照)与资质证照管理(营业执照/食品经营许可证/法人身份证),完善全字段必填校验。
|
||||
- **`din-canteen.html`**(场所管理 · 场所列表):列表新增「负责人」「营业时段」列;详情抽屉重构为 4 个分区(基本信息 / 营业时段 / 场所图片 / 资质信息),Mock 数据补齐营业时段及证照信息。
|
||||
- **`din-ingredient.html`**(食物成分):新增「食材分类管理」弹窗,支持两级树形分类的增删改查,含子级删除校验及 11 个一级分类示例数据。
|
||||
- **`din-dish-all.html`**(餐品总库):新增「餐品分类管理」弹窗,支持一级分类的增删改查及 8 个分类示例数据。
|
||||
- **`din-ingredient-add.html`**(食物成分 · 新增/编辑食材):基本信息区新增「从已有食材复制」按钮,弹窗搜索选择已有食材后自动填充全部表单(排除名称/分类/别名/图片),覆盖营养成分/味道/口感/色泽档案全部步骤,编辑模式下排除当前食材自身。
|
||||
|
||||
### 营养管理小程序
|
||||
|
||||
- **`eat-order.html`**(订餐页):净菜包菜品名称去掉「净菜包」后缀;菜品图标、名称、描述区域点击可跳转至菜品详情页。
|
||||
- **`eat-order-confirm.html`**(订单确认页 · 净菜包):期望配送时间由 chip 单选改为日期+时段联动下拉面板,左侧日期卡片右侧时段列表;取货方式切换时时间标签文案联动(期望配送时间 ↔ 自提时间)。
|
||||
- **`me-health.html`**(健康档案):移除「营养目标」分区及其编辑弹窗。
|
||||
- **`me-home.html`**(我的主页):菜单列表新增「收货地址」入口,指向新增的 `me-address.html`。
|
||||
- **`me-address.html`**(收货地址 · 新增页面):支持地址卡片列表展示、编辑/删除/设为默认操作,含内联编辑表单。
|
||||
- **`me-order-detail.html`**(订单详情 · 净菜包):按订单状态(待配送/配送中/已完成)差异化渲染——头部徽标文案与配色联动,待配送状态仅展示食材明细+主辅材角色标签,已发货/已完成展示食材来源及溯源按钮;订单进度时间线改为动态渲染,按状态高亮对应节点。
|
||||
- **`me-orders.html`**(我的订单):净菜包订单卡片跳转链接补充 status 参数,支持按状态进入对应详情。
|
||||
|
||||
## [2026-07-14] feat: 净菜包购买链路补齐与健康档案重构
|
||||
|
||||
### 后台营养管理
|
||||
|
||||
Reference in New Issue
Block a user