文件

321 行
20 KiB
HTML

<style>
.srv-modal-wrap { padding: 4px 8px; color: #1e293b; }
.srv-nav-tabs {
border-bottom: 2px solid #e2e8f0;
margin-bottom: 16px;
display: flex;
gap: 8px;
}
.srv-nav-tabs > li > a {
border: none !important;
border-radius: 8px 8px 0 0 !important;
padding: 10px 20px !important;
font-size: 13.5px;
font-weight: 600;
color: #64748b !important;
background: #f8fafc !important;
margin-right: 0 !important;
}
.srv-nav-tabs > li.active > a,
.srv-nav-tabs > li.active > a:hover,
.srv-nav-tabs > li.active > a:focus {
color: #ea580c !important;
background: #fff7ed !important;
border-bottom: 2px solid #ea580c !important;
}
.srv-section-card {
background: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 10px;
padding: 16px 18px;
margin-bottom: 16px;
}
.srv-section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 12px;
margin-bottom: 14px;
border-bottom: 1px solid #f1f5f9;
}
.srv-section-title { font-size: 14px; font-weight: 700; color: #0f172a; display: flex; align-items: center; gap: 8px; }
.srv-section-title i { color: #ea580c; }
.srv-matrix-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;
}
.srv-matrix-table thead th {
padding: 10px 12px;
font-size: 12.5px;
font-weight: 700;
color: #334155;
border-bottom: 1px solid #e2e8f0;
background: #f8fafc;
}
.srv-matrix-table thead th.col-main-hd { background: #eff6ff; color: #1e40af; border-left: 1px solid #dbeafe; }
.srv-matrix-table thead th.col-addon-hd { background: #fff7ed; color: #9a3412; border-left: 1px solid #fed7aa; }
.srv-matrix-table tbody td {
padding: 10px 12px;
vertical-align: middle;
border-bottom: 1px solid #f1f5f9;
}
.srv-matrix-table tbody tr.is-row-enabled { background: #ffffff; }
.srv-matrix-table tbody tr.is-row-disabled { background: #f8fafc; opacity: 0.75; }
.srv-name-box { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.srv-name-text { font-size: 13.5px; font-weight: 700; color: #0f172a; white-space: nowrap; }
.srv-code-badge { font-size: 11px; padding: 1px 6px; border-radius: 4px; background: #f1f5f9; color: #64748b; font-family: Consolas, monospace; }
.srv-matrix-toolbar { display: flex; align-items: center; justify-content: space-between; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 8px 12px; margin-bottom: 12px; }
.srv-filter-btn-group { display: flex; gap: 6px; }
.srv-filter-btn { border: 1px solid #cbd5e1; background: #fff; color: #475569; padding: 4px 12px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer; }
.srv-filter-btn.active { background: #0f172a; color: #fff; border-color: #0f172a; }
.srv-switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; margin: 0; }
.srv-switch input { display: none; }
.srv-switch .slider { width: 38px; height: 20px; background-color: #cbd5e1; border-radius: 20px; position: relative; transition: 0.2s; }
.srv-switch .slider::before { content: ""; position: absolute; width: 16px; height: 16px; left: 2px; bottom: 2px; background-color: white; border-radius: 50%; transition: 0.2s; }
.srv-switch.switch-blue input:checked + .slider { background-color: #2563eb; }
.srv-switch.switch-orange input:checked + .slider { background-color: #ea580c; }
.srv-switch input:checked + .slider::before { transform: translateX(18px); }
.srv-config-cell-main { border-left: 1px solid #f1f5f9; background: #fafcff; }
.srv-config-cell-addon { border-left: 1px solid #f1f5f9; background: #fffdf9; }
.srv-inline-controls { display: flex; align-items: center; gap: 10px; flex-wrap: nowrap; }
.srv-off-hint { font-size: 12px; color: #94a3b8; margin-left: 8px; }
.srv-quote-badge { display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; border-radius: 6px; background: #f1f5f9; color: #475569; font-size: 12px; border: 1px dashed #cbd5e1; }
.srv-default-label { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: #475569; cursor: pointer; margin: 0; padding: 3px 8px; border-radius: 6px; }
.srv-default-label.is-def-active { background: #dbeafe; color: #1d4ed8; font-weight: 600; }
</style>
<form id="add-form" class="form-horizontal srv-modal-wrap" role="form" data-toggle="validator" method="POST" action="">
<ul class="nav nav-tabs srv-nav-tabs">
<li class="active"><a href="#tab-basic" data-toggle="tab"><i class="fa fa-cube"></i> 1. 商品基础属性与细分规格</a></li>
<li><a href="#tab-services" data-toggle="tab"><i class="fa fa-sliders"></i> 2. 关联服务与独立定价(主服务 / 增项服务)</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab-basic">
<div class="srv-section-card">
<div class="srv-section-header">
<div class="srv-section-title"><i class="fa fa-info-circle"></i> 商品标的物基础信息</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-4">所属服务类目:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-cat_id" data-rule="required" min="0" data-source="service/cat/index" class="form-control selectpage" name="row[cat_id]" type="text" value="" placeholder="请选择所属类目">
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-4">商品名称:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-name" data-rule="required" class="form-control" name="row[name]" type="text" value="" placeholder="如:坐便器、浴室柜、沙发">
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-4">数量单位:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-unit" data-rule="required" class="form-control" name="row[unit]" type="text" value="台" placeholder="如:台、套、张、个、扇">
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="form-group">
<label class="control-label col-xs-12 col-sm-4">步梯搬运楼层费:</label>
<div class="col-xs-12 col-sm-8">
<div class="row">
<div class="col-xs-6">
<div class="input-group">
<span class="input-group-addon">¥</span>
<input id="c-floor_fee" data-rule="required" class="form-control" step="0.01" name="row[floor_fee]" type="number" value="10.00">
<span class="input-group-addon">/层</span>
</div>
</div>
<div class="col-xs-6">
<div class="input-group">
<span class="input-group-addon">免</span>
<input id="c-free_floor" data-rule="required" class="form-control" name="row[free_floor]" type="number" value="2">
<span class="input-group-addon">层</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<label class="control-label col-xs-12 col-sm-2">商品缩略图:</label>
<div class="col-xs-12 col-sm-10">
<div class="input-group">
<input id="c-image" class="form-control" size="50" name="row[image]" type="text" value="">
<div class="input-group-addon no-border no-padding">
<span><button type="button" id="faupload-image" class="btn btn-danger faupload plupload" data-category="service" data-input-id="c-image" data-mimetype="image/gif,image/jpeg,image/png,image/jpg,image/bmp,image/webp" data-multiple="false" data-preview-id="p-image"><i class="fa fa-upload"></i> 上传图片</button></span>
<span><button type="button" id="fachoose-image" class="btn btn-primary fachoose" data-input-id="c-image" data-mimetype="image/*" data-multiple="false"><i class="fa fa-list"></i> 图库选择</button></span>
</div>
<span class="msg-box n-right" for="c-image"></span>
</div>
<p class="help-block" style="margin:6px 0 0;font-size:12px;color:#64748b;">上传后同时作为小程序「系统图库」该商品的默认参考图;如需配置多款参考图,可在下方细分规格上传或前往「系统图库」管理。</p>
<ul class="row list-inline faupload-preview plupload-preview" id="p-image"></ul>
</div>
</div>
<div class="row">
<div class="col-xs-12 col-sm-6">
<div class="form-group" style="margin-bottom:0;">
<label class="control-label col-xs-12 col-sm-4">排序权重:</label>
<div class="col-xs-12 col-sm-8">
<input id="c-weigh" data-rule="required" class="form-control" name="row[weigh]" type="number" value="100">
</div>
</div>
</div>
<div class="col-xs-12 col-sm-6">
<div class="form-group" style="margin-bottom:0;">
<label class="control-label col-xs-12 col-sm-4">上架状态:</label>
<div class="col-xs-12 col-sm-8">
<div class="radio">
{foreach name="statusList" item="vo"}
<label for="row[status]-{$key}"><input id="row[status]-{$key}" name="row[status]" type="radio" value="{$key}" {in name="key" value="normal"}checked{/in} /> {$vo}</label>
{/foreach}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="srv-section-card">
<div class="srv-section-header">
<div class="srv-section-title">
<i class="fa fa-tags"></i> 商品细分型号与款式规格
<small class="text-muted" style="font-weight:400;font-size:12px;">(常规规格留空价格即可,自动沿用商品默认服务价;仅施工难度特殊的规格需填专属一口价)</small>
</div>
<button type="button" class="btn btn-xs btn-success" id="btn-add-spec"><i class="fa fa-plus"></i> 新增细分规格</button>
</div>
<table class="table table-bordered table-hover" id="specs-table" style="margin-bottom: 0;">
<thead>
<tr class="active">
<th style="width: 50px; text-align: center;">序号</th>
<th style="width: 220px;">细分规格/款式名称</th>
<th style="width: 155px; text-align: center;">规格参考图(选填)</th>
<th style="width: 195px;">特殊规格一口价(选填)</th>
<th>规格说明或施工提示(选填)</th>
<th style="width: 65px; text-align: center;">操作</th>
</tr>
</thead>
<tbody id="specs-tbody"></tbody>
</table>
</div>
</div>
<div class="tab-pane" id="tab-services">
<div class="srv-matrix-toolbar">
<div class="srv-filter-btn-group">
<button type="button" class="srv-filter-btn active" data-filter="all">全部服务字典 ({:count($typesPool)})</button>
<button type="button" class="srv-filter-btn" data-filter="enabled">仅看已启用服务</button>
</div>
<div style="display:flex;align-items:center;gap:10px;">
<input type="text" id="srv-matrix-search" class="form-control input-sm" style="width:210px;border-radius:6px;" placeholder="搜索服务名称或标识...">
</div>
</div>
<table class="srv-matrix-table" id="service-matrix-table">
<thead>
<tr>
<th style="width: 230px;">全局服务项</th>
<th class="col-main-hd" style="width: 430px;">作为【主服务项目】配置(用户单独呼叫该服务时的价格)</th>
<th class="col-addon-hd">作为【增项服务】配置(顺带加选时的加价)</th>
</tr>
</thead>
<tbody>
{foreach name="typesPool" item="tp"}
<?php
$code = $tp['code'];
$name = $tp['name'];
$unit = $tp['unit'] ?: '台';
$isMainChecked = ($tp['can_be_main'] == 1 && in_array($code, ['install','repair','remove']));
$mainSupportFixed = (int)$tp['support_fixed'];
$mainPrice = (float)$tp['default_price'];
$isMainDefault = (int)$tp['is_default'];
$isAddonChecked = ($code === 'remove');
$addonPrice = (float)$tp['default_addon_price'] > 0 ? (float)$tp['default_addon_price'] : 30.00;
$rowEnabled = ($isMainChecked || $isAddonChecked);
?>
<tr class="srv-matrix-row {if $rowEnabled}is-row-enabled{else/}is-row-disabled{/if}" data-id="{$tp.id}" data-code="{$code}" data-name="{$name}" data-unit="{$unit}" data-support-fixed="{$mainSupportFixed}">
<td>
<div class="srv-name-box">
<span class="srv-name-text">{$name}</span>
<span class="srv-code-badge">{$code}</span>
</div>
</td>
<td class="srv-config-cell-main">
<div class="srv-inline-controls">
<label class="srv-switch switch-blue">
<input type="checkbox" class="st-enable" data-code="{$code}" {if $isMainChecked}checked{/if}>
<span class="slider"></span>
</label>
<div class="st-active-panel" style="display: {if $isMainChecked}inline-flex{else/}none{/if}; align-items: center; gap: 10px;">
{if $mainSupportFixed == 1}
<div class="input-group input-group-sm st-price-wrap" style="width: 170px;">
<span class="input-group-addon" style="background:#eff6ff;color:#1d4ed8;font-weight:700;">¥</span>
<input type="number" step="0.01" class="form-control st-price" data-code="{$code}" value="{$mainPrice}" style="font-weight:700;color:#0f172a;">
<span class="input-group-addon">/{$unit}</span>
</div>
<label class="srv-default-label {if $isMainDefault==1}is-def-active{/if}">
<input type="radio" name="st_default_radio" class="st-default" data-code="{$code}" {if $isMainDefault==1}checked{/if}> 默认首选
</label>
{else/}
<span class="srv-quote-badge">不支持一口价(下单时自动转师傅报价)</span>
{/if}
</div>
<span class="srv-off-hint st-off-hint" style="display: {if $isMainChecked}none{else/}inline{/if};">未启用主服务</span>
</div>
</td>
<td class="srv-config-cell-addon">
<div class="srv-inline-controls">
<label class="srv-switch switch-orange">
<input type="checkbox" class="ad-bind" data-code="{$code}" {if $isAddonChecked}checked{/if}>
<span class="slider"></span>
</label>
<div class="ad-active-panel" style="display: {if $isAddonChecked}inline-flex{else/}none{/if}; align-items: center; gap: 8px;">
<div class="input-group input-group-sm" style="width: 175px;">
<span class="input-group-addon" style="background:#fff7ed;color:#ea580c;font-weight:700;">+¥</span>
<input type="number" step="0.01" class="form-control ad-price" data-code="{$code}" value="{$addonPrice}" style="font-weight:700;color:#ea580c;">
<span class="input-group-addon">/{$unit}</span>
</div>
</div>
<span class="srv-off-hint ad-off-hint" style="display: {if $isAddonChecked}none{else/}inline{/if};">未作为增项启用</span>
</div>
</td>
</tr>
{/foreach}
</tbody>
</table>
<input type="hidden" name="row[service_types_raw]" id="service_types_raw" value="">
<input type="hidden" name="row[addons_raw]" id="addons_raw" value="">
</div>
</div>
<div class="form-group layer-footer">
<label class="control-label col-xs-12 col-sm-2"></label>
<div class="col-xs-12 col-sm-8">
<button type="submit" class="btn btn-primary btn-embossed disabled" id="btn-submit-service"><i class="fa fa-check"></i> 保存商品与服务定价</button>
<button type="reset" class="btn btn-default btn-embossed">{:__('Reset')}</button>
</div>
</div>
</form>