Commit d1b7cbd4 by suyuchen

feat(service-pricing): 添加服务价格标准化管理系统前端页面

- 创建服务价格标准首页,包含统计卡片和快捷入口功能 - 实现人工费用标准管理列表页,支持查看和编辑人工费用标准 - 开发人工费用标准新增编辑页,提供完整的表单功能 - 添加零件费用标准管理列表页,支持零件费用标准的查看 - 实现零件费用标准新增编辑页,包含基础信息录入功能
parent 507e83cb
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>服务价格标准首页 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.card {
background: white;
border-radius: 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
color: white;
padding: 25px;
border-radius: 8px;
text-align: center;
transition: transform 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
}
.stat-card.success {
background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
}
.stat-card.warning {
background: linear-gradient(135deg, #e6a23c 0%, #ebb563 100%);
}
.stat-card.info {
background: linear-gradient(135deg, #909399 0%, #b1b3b8 100%);
}
.stat-value {
font-size: 36px;
font-weight: bold;
margin: 15px 0;
}
.stat-label {
font-size: 14px;
opacity: 0.9;
}
.whitebook-info {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
border: 2px solid #409eff;
border-radius: 8px;
padding: 20px;
margin-bottom: 30px;
}
.whitebook-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.whitebook-title {
font-size: 20px;
font-weight: 600;
color: #333;
}
.whitebook-version {
font-size: 18px;
color: #409eff;
font-weight: bold;
}
.whitebook-status {
display: inline-block;
padding: 4px 12px;
background: #67c23a;
color: white;
border-radius: 4px;
font-size: 12px;
}
.quick-actions {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
}
.action-btn {
padding: 20px;
background: white;
border: 2px solid #e4e7ed;
border-radius: 8px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
text-decoration: none;
color: #333;
display: block;
}
.action-btn:hover {
border-color: #409eff;
background: #f0f9ff;
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
}
.action-icon {
font-size: 32px;
margin-bottom: 10px;
}
.action-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 5px;
}
.action-desc {
font-size: 12px;
color: #909399;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<h1>服务价格标准化管理系统</h1>
</div>
<!-- 白皮书信息 -->
<div class="whitebook-info">
<div class="whitebook-header">
<div>
<div class="whitebook-title">当前生效的服务价格白皮书</div>
<div style="margin-top: 10px; color: #666; font-size: 14px;">版本号:<span class="whitebook-version">{{ whitebook.version }}</span></div>
</div>
<span class="whitebook-status">{{ whitebook.status }}</span>
</div>
<div style="color: #666; font-size: 14px;">生效时间:{{ whitebook.effectiveDate }}</div>
</div>
<!-- 统计卡片 -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">人工费用标准数量</div>
<div class="stat-value">{{ stats.laborCount }}</div>
<div style="font-size: 12px; margin-top: 10px; opacity: 0.8;">已配置标准</div>
</div>
<div class="stat-card success">
<div class="stat-label">零件费用标准数量</div>
<div class="stat-value">{{ stats.partCount }}</div>
<div style="font-size: 12px; margin-top: 10px; opacity: 0.8;">已配置标准</div>
</div>
<div class="stat-card warning">
<div class="stat-label">已启用的报价规则</div>
<div class="stat-value">{{ stats.ruleCount }}</div>
<div style="font-size: 12px; margin-top: 10px; opacity: 0.8;">启用中</div>
</div>
</div>
<!-- 快捷入口 -->
<div class="card">
<div class="card-title">快捷入口</div>
<div class="quick-actions">
<a href="2-人工费用标准管理列表页.html" class="action-btn">
<div class="action-icon">👷</div>
<div class="action-title">人工费用管理</div>
<div class="action-desc">管理服务人工费用标准</div>
</a>
<a href="4-零件费用标准管理列表页.html" class="action-btn">
<div class="action-icon">🔧</div>
<div class="action-title">零件费用管理</div>
<div class="action-desc">管理零件费用标准</div>
</a>
<a href="6-报价规则配置列表页.html" class="action-btn">
<div class="action-icon">⚙️</div>
<div class="action-title">报价规则配置</div>
<div class="action-desc">配置报价规则</div>
</a>
<a href="10-服务价格白皮书查看页.html" class="action-btn">
<div class="action-icon">📄</div>
<div class="action-title">查看服务价格白皮书</div>
<div class="action-desc">查看完整价格标准文档</div>
</a>
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
whitebook: {
version: 'V1.0',
status: '生效中',
effectiveDate: '2024-01-01'
},
stats: {
laborCount: 12,
partCount: 156,
ruleCount: 8
}
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>服务价格白皮书 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.readonly-badge {
display: inline-block;
padding: 4px 12px;
background: #fef0f0;
color: #f56c6c;
border: 1px solid #f56c6c;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
margin-left: 15px;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 25px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.whitebook-header {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
border: 2px solid #409eff;
border-radius: 8px;
padding: 25px;
margin-bottom: 30px;
text-align: center;
}
.whitebook-title {
font-size: 28px;
font-weight: 600;
color: #333;
margin-bottom: 15px;
}
.whitebook-version {
font-size: 20px;
color: #409eff;
font-weight: 600;
margin-bottom: 10px;
}
.whitebook-meta {
font-size: 14px;
color: #606266;
margin-top: 10px;
}
.section {
margin-bottom: 40px;
}
.section-title {
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #e4e7ed;
}
.section-content {
line-height: 1.8;
color: #606266;
font-size: 14px;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th,
.data-table td {
padding: 12px;
text-align: left;
border: 1px solid #e4e7ed;
}
.data-table th {
background: #f5f7fa;
font-weight: 600;
color: #333;
}
.data-table tr:nth-child(even) {
background: #fafafa;
}
.info-box {
background: #f5f7fa;
border-left: 4px solid #409eff;
padding: 15px 20px;
margin: 20px 0;
border-radius: 4px;
}
.info-box-title {
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.highlight {
color: #409eff;
font-weight: 600;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<div class="header-top">
<div>
<h1>服务价格白皮书 <span class="readonly-badge">只读模式</span></h1>
</div>
<button class="btn btn-default" @click="handleBack">返回首页</button>
</div>
</div>
<div class="card">
<!-- 白皮书头部 -->
<div class="whitebook-header">
<div class="whitebook-title">服务价格白皮书</div>
<div class="whitebook-version">版本号:{{ whitebook.version }}</div>
<div class="whitebook-meta">
<div>生效时间:{{ whitebook.effectiveDate }}</div>
<div>状态:<span class="highlight">{{ whitebook.status }}</span></div>
</div>
</div>
<!-- 适用范围说明 -->
<div class="section">
<div class="section-title">一、适用范围说明</div>
<div class="section-content">
<p>本服务价格白皮书适用于质保期外的所有服务项目,包括但不限于:</p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>设备维修服务</li>
<li>技术支持服务</li>
<li>上门服务</li>
<li>远程服务</li>
<li>紧急服务</li>
</ul>
<div class="info-box">
<div class="info-box-title">重要提示</div>
<div>本白皮书中的所有价格标准均为质保期外服务的标准价格,质保期内服务按照合同约定执行。</div>
</div>
</div>
</div>
<!-- 人工费用标准 -->
<div class="section">
<div class="section-title">二、人工费用标准</div>
<div class="section-content">
<p>人工费用根据服务类型、技术等级和计费方式的不同,执行以下标准:</p>
<table class="data-table">
<thead>
<tr>
<th>服务类型</th>
<th>技术等级</th>
<th>计费方式</th>
<th>单价</th>
<th>最低计费时长</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in laborStandards" :key="index">
<td>{{ item.serviceType }}</td>
<td>{{ item.techLevel }}</td>
<td>{{ item.billingMethod }}</td>
<td>¥{{ item.price }}/{{ item.billingMethod === '按小时' ? '小时' : '次' }}</td>
<td>{{ item.minHours || '-' }}</td>
<td>{{ item.description }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 零件费用说明 -->
<div class="section">
<div class="section-title">三、零件费用说明</div>
<div class="section-content">
<p>零件费用按照标准零件价格表执行,主要包括:</p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>零件价格以系统标准价格表为准</li>
<li>零件价格可能因市场波动而调整,以实际报价时价格为准</li>
<li>部分零件价格含税,部分不含税,具体以报价单为准</li>
<li>零件数量以实际使用为准</li>
</ul>
<div class="info-box" style="margin-top: 20px;">
<div class="info-box-title">零件价格查询</div>
<div>详细的零件价格标准请参考系统中的零件费用标准管理模块,或联系客服获取最新价格信息。</div>
</div>
</div>
</div>
<!-- 特殊/加急服务价格说明 -->
<div class="section">
<div class="section-title">四、特殊/加急服务价格说明</div>
<div class="section-content">
<p>对于紧急服务或特殊时间段的服务,可能产生额外的加急费用:</p>
<table class="data-table">
<thead>
<tr>
<th>服务类型</th>
<th>加急费类型</th>
<th>加急费标准</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>紧急服务</td>
<td>百分比</td>
<td>加收20%</td>
<td>在标准人工费用基础上加收20%</td>
</tr>
<tr>
<td>非工作时间服务</td>
<td>百分比</td>
<td>加收30%</td>
<td>节假日、夜间等非工作时间提供服务</td>
</tr>
<tr>
<td>超远距离服务</td>
<td>固定金额</td>
<td>加收500元</td>
<td>距离超过50公里的上门服务</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- 报价说明 -->
<div class="section">
<div class="section-title">五、报价说明</div>
<div class="section-content">
<p>标准报价单的生成遵循以下规则:</p>
<ol style="margin-left: 20px; margin-top: 10px;">
<li><strong>自动匹配规则:</strong>系统根据服务场景(停机/非停机)自动匹配相应的报价规则</li>
<li><strong>人工费用计算:</strong>根据选择的服务类型和技术等级,按照标准价格计算</li>
<li><strong>零件费用计算:</strong>根据实际使用的零件数量和标准价格计算</li>
<li><strong>加急费用:</strong>如适用,按照加急费规则计算并累加</li>
<li><strong>折扣规则:</strong>如适用,按照折扣规则进行减免</li>
<li><strong>最终价格:</strong>所有费用项目相加得出最终报价</li>
</ol>
<div class="info-box" style="margin-top: 20px;">
<div class="info-box-title">报价有效期</div>
<div>标准报价单的有效期为30天,超过有效期需重新生成报价单。最终价格以实际服务完成后的结算为准。</div>
</div>
</div>
</div>
<!-- 数据来源说明 -->
<div class="section">
<div class="section-title">六、数据来源说明</div>
<div class="section-content">
<p>本白皮书中的所有价格数据均来源于系统价格配置模块:</p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>人工费用标准:来源于"人工费用标准管理"模块</li>
<li>零件费用标准:来源于"零件费用标准管理"模块</li>
<li>报价规则:来源于"报价规则配置"模块</li>
</ul>
<div class="info-box" style="margin-top: 20px;">
<div class="info-box-title">重要提示</div>
<div>本白皮书为只读文档,所有价格配置的修改需在相应的管理模块中进行。价格调整后,系统将自动更新白皮书版本。</div>
</div>
</div>
</div>
<!-- 联系方式 -->
<div class="section">
<div class="section-title">七、联系方式</div>
<div class="section-content">
<p>如有任何疑问或需要了解更多信息,请联系:</p>
<ul style="margin-left: 20px; margin-top: 10px;">
<li>客服热线:400-XXX-XXXX</li>
<li>技术支持邮箱:support@example.com</li>
<li>工作时间:周一至周五 9:00-18:00</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
whitebook: {
version: 'V1.0',
effectiveDate: '2024-01-01',
status: '生效中'
},
laborStandards: [
{
serviceType: '上门',
techLevel: '普通',
billingMethod: '按小时',
price: 150,
minHours: 2,
description: '标准上门服务,最低计费2小时'
},
{
serviceType: '上门',
techLevel: '高级',
billingMethod: '按小时',
price: 250,
minHours: 2,
description: '高级技术上门服务,最低计费2小时'
},
{
serviceType: '远程',
techLevel: '普通',
billingMethod: '按次',
price: 200,
minHours: null,
description: '远程技术支持服务'
},
{
serviceType: '远程',
techLevel: '高级',
billingMethod: '按次',
price: 300,
minHours: null,
description: '高级远程技术支持服务'
},
{
serviceType: '紧急',
techLevel: '普通',
billingMethod: '按小时',
price: 300,
minHours: 1,
description: '紧急上门服务,最低计费1小时'
},
{
serviceType: '紧急',
techLevel: '高级',
billingMethod: '按小时',
price: 400,
minHours: 1,
description: '高级紧急上门服务,最低计费1小时'
}
]
}
},
methods: {
handleBack() {
window.location.href = '1-服务价格标准首页.html';
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人工费用标准管理 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.nav-menu {
display: flex;
gap: 10px;
}
.nav-item {
padding: 8px 16px;
background: #409eff;
color: white;
text-decoration: none;
border-radius: 6px;
font-size: 14px;
transition: all 0.3s;
}
.nav-item:hover {
background: #66b1ff;
}
.nav-item.active {
background: #67c23a;
}
.card {
background: white;
border-radius: 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-success {
background: #67c23a;
color: white;
}
.btn-success:hover {
background: #85ce61;
}
.btn-danger {
background: #f56c6c;
color: white;
}
.btn-danger:hover {
background: #f78989;
}
.btn-warning {
background: #e6a23c;
color: white;
}
.btn-warning:hover {
background: #ebb563;
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th,
.data-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e4e7ed;
}
.data-table th {
background: #f5f7fa;
font-weight: 600;
color: #333;
}
.data-table tr:hover {
background: #f5f7fa;
}
.badge {
display: inline-block;
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.badge-success {
background: #f0f9ff;
color: #67c23a;
border: 1px solid #67c23a;
}
.badge-danger {
background: #fef0f0;
color: #f56c6c;
border: 1px solid #f56c6c;
}
.action-btns {
display: flex;
gap: 8px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<div class="header-top">
<h1>人工费用标准管理</h1>
<div class="nav-menu">
<a href="1-服务价格标准首页.html" class="nav-item">首页</a>
<a href="2-人工费用标准管理列表页.html" class="nav-item active">人工费用</a>
<a href="4-零件费用标准管理列表页.html" class="nav-item">零件费用</a>
<a href="6-报价规则配置列表页.html" class="nav-item">报价规则</a>
</div>
</div>
</div>
<div class="card">
<div class="toolbar">
<div class="card-title">人工费用标准列表</div>
<button class="btn btn-primary" @click="handleAdd">+ 新增人工费用</button>
</div>
<table class="data-table">
<thead>
<tr>
<th>服务类型</th>
<th>技术等级</th>
<th>计费方式</th>
<th>单价</th>
<th>最低计费时长</th>
<th>生效时间</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in laborList" :key="item.id">
<td>{{ item.serviceType }}</td>
<td>{{ item.techLevel }}</td>
<td>{{ item.billingMethod }}</td>
<td>¥{{ item.price }}/{{ item.billingMethod === '按小时' ? '小时' : '次' }}</td>
<td>{{ item.minHours || '-' }}</td>
<td>{{ item.effectiveDate }}</td>
<td>
<span :class="item.status === '启用' ? 'badge badge-success' : 'badge badge-danger'">
{{ item.status }}
</span>
</td>
<td>
<div class="action-btns">
<button class="btn btn-primary btn-small" @click="handleEdit(item)">编辑</button>
<button
v-if="item.status === '启用'"
class="btn btn-warning btn-small"
@click="handleToggleStatus(item)">
停用
</button>
<button
v-else
class="btn btn-success btn-small"
@click="handleToggleStatus(item)">
启用
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
laborList: [
{
id: 1,
serviceType: '上门',
techLevel: '普通',
billingMethod: '按小时',
price: 150,
minHours: 2,
effectiveDate: '2024-01-01',
status: '启用'
},
{
id: 2,
serviceType: '上门',
techLevel: '高级',
billingMethod: '按小时',
price: 250,
minHours: 2,
effectiveDate: '2024-01-01',
status: '启用'
},
{
id: 3,
serviceType: '远程',
techLevel: '普通',
billingMethod: '按次',
price: 200,
minHours: null,
effectiveDate: '2024-01-01',
status: '启用'
},
{
id: 4,
serviceType: '紧急',
techLevel: '高级',
billingMethod: '按小时',
price: 400,
minHours: 1,
effectiveDate: '2024-01-01',
status: '停用'
}
]
}
},
methods: {
handleAdd() {
window.location.href = '3-人工费用标准新增编辑页.html?action=add';
},
handleEdit(item) {
window.location.href = `3-人工费用标准新增编辑页.html?action=edit&id=${item.id}`;
},
handleToggleStatus(item) {
item.status = item.status === '启用' ? '停用' : '启用';
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人工费用标准{{ isEdit ? '编辑' : '新增' }} - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 25px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
.form-label .required {
color: #f56c6c;
margin-left: 4px;
}
.form-control {
width: 100%;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #409eff;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 15px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e4e7ed;
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.form-hint {
font-size: 12px;
color: #909399;
margin-top: 5px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<h1>人工费用标准{{ isEdit ? '编辑' : '新增' }}</h1>
</div>
<div class="card">
<div class="card-title">基本信息</div>
<form @submit.prevent="handleSubmit">
<div class="form-row">
<div class="form-group">
<label class="form-label">服务类型 <span class="required">*</span></label>
<select v-model="form.serviceType" class="form-control" required>
<option value="">请选择</option>
<option value="上门">上门</option>
<option value="远程">远程</option>
<option value="紧急">紧急</option>
</select>
</div>
<div class="form-group">
<label class="form-label">技术等级 <span class="required">*</span></label>
<select v-model="form.techLevel" class="form-control" required>
<option value="">请选择</option>
<option value="普通">普通</option>
<option value="高级">高级</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">计费方式 <span class="required">*</span></label>
<select v-model="form.billingMethod" class="form-control" required>
<option value="">请选择</option>
<option value="按小时">按小时</option>
<option value="按次">按次</option>
</select>
</div>
<div class="form-group">
<label class="form-label">单价 <span class="required">*</span></label>
<input
type="number"
v-model.number="form.price"
class="form-control"
placeholder="请输入单价"
step="0.01"
min="0"
required
/>
<div class="form-hint">单位:元/{{ form.billingMethod || '次或小时' }}</div>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">最低计费时长</label>
<input
type="number"
v-model.number="form.minHours"
class="form-control"
placeholder="请输入最低计费时长"
min="0"
step="0.5"
/>
<div class="form-hint">仅按小时计费时有效,单位:小时</div>
</div>
<div class="form-group">
<label class="form-label">生效时间 <span class="required">*</span></label>
<input
type="date"
v-model="form.effectiveDate"
class="form-control"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">失效时间</label>
<input
type="date"
v-model="form.expiryDate"
class="form-control"
/>
<div class="form-hint">留空表示长期有效</div>
</div>
</div>
<div class="form-group">
<label class="form-label">备注</label>
<textarea
v-model="form.remark"
class="form-control"
rows="4"
placeholder="请输入备注信息"
></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn btn-default" @click="handleCancel">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
const urlParams = new URLSearchParams(window.location.search);
const action = urlParams.get('action');
const id = urlParams.get('id');
// 模拟编辑数据
const editData = {
1: {
serviceType: '上门',
techLevel: '普通',
billingMethod: '按小时',
price: 150,
minHours: 2,
effectiveDate: '2024-01-01',
expiryDate: '',
remark: '标准上门服务费用'
}
};
return {
isEdit: action === 'edit',
form: {
serviceType: '',
techLevel: '',
billingMethod: '',
price: null,
minHours: null,
effectiveDate: '',
expiryDate: '',
remark: ''
}
}
},
mounted() {
if (this.isEdit) {
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get('id');
// 模拟加载数据
if (id === '1') {
this.form = {
serviceType: '上门',
techLevel: '普通',
billingMethod: '按小时',
price: 150,
minHours: 2,
effectiveDate: '2024-01-01',
expiryDate: '',
remark: '标准上门服务费用'
};
}
}
},
methods: {
handleSubmit() {
alert(this.isEdit ? '保存成功!' : '新增成功!');
window.location.href = '2-人工费用标准管理列表页.html';
},
handleCancel() {
if (confirm('确定要取消吗?未保存的数据将丢失。')) {
window.location.href = '2-人工费用标准管理列表页.html';
}
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>零件费用标准管理 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.nav-menu {
display: flex;
gap: 10px;
}
.nav-item {
padding: 8px 16px;
background: #409eff;
color: white;
text-decoration: none;
border-radius: 6px;
font-size: 14px;
transition: all 0.3s;
}
.nav-item:hover {
background: #66b1ff;
}
.nav-item.active {
background: #67c23a;
}
.card {
background: white;
border-radius: 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-success {
background: #67c23a;
color: white;
}
.btn-success:hover {
background: #85ce61;
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th,
.data-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e4e7ed;
}
.data-table th {
background: #f5f7fa;
font-weight: 600;
color: #333;
}
.data-table tr:hover {
background: #f5f7fa;
}
.badge {
display: inline-block;
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.badge-success {
background: #f0f9ff;
color: #67c23a;
border: 1px solid #67c23a;
}
.badge-danger {
background: #fef0f0;
color: #f56c6c;
border: 1px solid #f56c6c;
}
.action-btns {
display: flex;
gap: 8px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<div class="header-top">
<h1>零件费用标准管理</h1>
<div class="nav-menu">
<a href="1-服务价格标准首页.html" class="nav-item">首页</a>
<a href="2-人工费用标准管理列表页.html" class="nav-item">人工费用</a>
<a href="4-零件费用标准管理列表页.html" class="nav-item active">零件费用</a>
<a href="6-报价规则配置列表页.html" class="nav-item">报价规则</a>
</div>
</div>
</div>
<div class="card">
<div class="toolbar">
<div class="card-title">零件费用标准列表</div>
<div style="display: flex; gap: 10px;">
<button class="btn btn-success" @click="handleImport">批量导入</button>
<button class="btn btn-primary" @click="handleAdd">+ 新增零件</button>
</div>
</div>
<table class="data-table">
<thead>
<tr>
<th>零件编号</th>
<th>零件名称</th>
<th>型号规格</th>
<th>标准单价</th>
<th>单位</th>
<th>适用设备</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in partList" :key="item.id">
<td>{{ item.partNo }}</td>
<td>{{ item.partName }}</td>
<td>{{ item.specification }}</td>
<td>¥{{ item.price }}</td>
<td>{{ item.unit }}</td>
<td>{{ item.applicableEquipment }}</td>
<td>
<span :class="item.status === '启用' ? 'badge badge-success' : 'badge badge-danger'">
{{ item.status }}
</span>
</td>
<td>
<div class="action-btns">
<button class="btn btn-primary btn-small" @click="handleEdit(item)">编辑</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
partList: [
{
id: 1,
partNo: 'P001',
partName: '服务器硬盘',
specification: '2TB SATA',
price: 800,
unit: '块',
applicableEquipment: '服务器',
status: '启用'
},
{
id: 2,
partNo: 'P002',
partName: '内存条',
specification: '16GB DDR4',
price: 600,
unit: '条',
applicableEquipment: '服务器/工作站',
status: '启用'
},
{
id: 3,
partNo: 'P003',
partName: '网络交换机',
specification: '24口千兆',
price: 2000,
unit: '台',
applicableEquipment: '网络设备',
status: '启用'
},
{
id: 4,
partNo: 'P004',
partName: 'UPS电源',
specification: '3KVA',
price: 3500,
unit: '台',
applicableEquipment: '电源设备',
status: '启用'
}
]
}
},
methods: {
handleAdd() {
window.location.href = '5-零件费用标准新增编辑页.html?action=add';
},
handleEdit(item) {
window.location.href = `5-零件费用标准新增编辑页.html?action=edit&id=${item.id}`;
},
handleImport() {
alert('批量导入功能(占位)');
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>零件费用标准{{ isEdit ? '编辑' : '新增' }} - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 25px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
.form-label .required {
color: #f56c6c;
margin-left: 4px;
}
.form-control {
width: 100%;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #409eff;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 15px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e4e7ed;
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.form-hint {
font-size: 12px;
color: #909399;
margin-top: 5px;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #409eff;
}
input:checked + .slider:before {
transform: translateX(26px);
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<h1>零件费用标准{{ isEdit ? '编辑' : '新增' }}</h1>
</div>
<div class="card">
<div class="card-title">基本信息</div>
<form @submit.prevent="handleSubmit">
<div class="form-row">
<div class="form-group">
<label class="form-label">零件编号 <span class="required">*</span></label>
<input
type="text"
v-model="form.partNo"
class="form-control"
placeholder="请输入零件编号"
required
/>
</div>
<div class="form-group">
<label class="form-label">零件名称 <span class="required">*</span></label>
<input
type="text"
v-model="form.partName"
class="form-control"
placeholder="请输入零件名称"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">型号规格 <span class="required">*</span></label>
<input
type="text"
v-model="form.specification"
class="form-control"
placeholder="请输入型号规格"
required
/>
</div>
<div class="form-group">
<label class="form-label">单位 <span class="required">*</span></label>
<input
type="text"
v-model="form.unit"
class="form-control"
placeholder="如:块、条、台等"
required
/>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">标准单价 <span class="required">*</span></label>
<input
type="number"
v-model.number="form.price"
class="form-control"
placeholder="请输入标准单价"
step="0.01"
min="0"
required
/>
<div class="form-hint">单位:元</div>
</div>
<div class="form-group">
<label class="form-label">是否含税</label>
<div style="margin-top: 8px;">
<label class="switch">
<input type="checkbox" v-model="form.taxIncluded">
<span class="slider"></span>
</label>
<span style="margin-left: 10px; color: #606266;">{{ form.taxIncluded ? '含税' : '不含税' }}</span>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">生效时间 <span class="required">*</span></label>
<input
type="date"
v-model="form.effectiveDate"
class="form-control"
required
/>
</div>
<div class="form-group">
<label class="form-label">备注</label>
<textarea
v-model="form.remark"
class="form-control"
rows="4"
placeholder="请输入备注信息"
></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn btn-default" @click="handleCancel">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
const urlParams = new URLSearchParams(window.location.search);
const action = urlParams.get('action');
const id = urlParams.get('id');
return {
isEdit: action === 'edit',
form: {
partNo: '',
partName: '',
specification: '',
unit: '',
price: null,
taxIncluded: true,
effectiveDate: '',
remark: ''
}
}
},
mounted() {
if (this.isEdit) {
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get('id');
// 模拟加载数据
if (id === '1') {
this.form = {
partNo: 'P001',
partName: '服务器硬盘',
specification: '2TB SATA',
unit: '块',
price: 800,
taxIncluded: true,
effectiveDate: '2024-01-01',
remark: '标准服务器硬盘'
};
}
}
},
methods: {
handleSubmit() {
alert(this.isEdit ? '保存成功!' : '新增成功!');
window.location.href = '4-零件费用标准管理列表页.html';
},
handleCancel() {
if (confirm('确定要取消吗?未保存的数据将丢失。')) {
window.location.href = '4-零件费用标准管理列表页.html';
}
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>报价规则配置 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1400px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.nav-menu {
display: flex;
gap: 10px;
}
.nav-item {
padding: 8px 16px;
background: #409eff;
color: white;
text-decoration: none;
border-radius: 6px;
font-size: 14px;
transition: all 0.3s;
}
.nav-item:hover {
background: #66b1ff;
}
.nav-item.active {
background: #67c23a;
}
.card {
background: white;
border-radius: 8px;
padding: 25px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-success {
background: #67c23a;
color: white;
}
.btn-success:hover {
background: #85ce61;
}
.btn-warning {
background: #e6a23c;
color: white;
}
.btn-warning:hover {
background: #ebb563;
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
}
.data-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.data-table th,
.data-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e4e7ed;
}
.data-table th {
background: #f5f7fa;
font-weight: 600;
color: #333;
}
.data-table tr:hover {
background: #f5f7fa;
}
.badge {
display: inline-block;
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.badge-success {
background: #f0f9ff;
color: #67c23a;
border: 1px solid #67c23a;
}
.badge-danger {
background: #fef0f0;
color: #f56c6c;
border: 1px solid #f56c6c;
}
.action-btns {
display: flex;
gap: 8px;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<div class="header-top">
<h1>报价规则配置</h1>
<div class="nav-menu">
<a href="1-服务价格标准首页.html" class="nav-item">首页</a>
<a href="2-人工费用标准管理列表页.html" class="nav-item">人工费用</a>
<a href="4-零件费用标准管理列表页.html" class="nav-item">零件费用</a>
<a href="6-报价规则配置列表页.html" class="nav-item active">报价规则</a>
</div>
</div>
</div>
<div class="card">
<div class="toolbar">
<div class="card-title">报价规则列表</div>
<button class="btn btn-primary" @click="handleAdd">+ 新增规则</button>
</div>
<table class="data-table">
<thead>
<tr>
<th>规则名称</th>
<th>适用场景</th>
<th>是否质保期外</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="item in ruleList" :key="item.id">
<td>{{ item.ruleName }}</td>
<td>{{ item.scenario }}</td>
<td></td>
<td>
<span :class="item.status === '启用' ? 'badge badge-success' : 'badge badge-danger'">
{{ item.status }}
</span>
</td>
<td>
<div class="action-btns">
<button class="btn btn-primary btn-small" @click="handleEdit(item)">编辑</button>
<button
v-if="item.status === '启用'"
class="btn btn-warning btn-small"
@click="handleToggleStatus(item)">
停用
</button>
<button
v-else
class="btn btn-success btn-small"
@click="handleToggleStatus(item)">
启用
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
ruleList: [
{
id: 1,
ruleName: '停机服务标准报价规则',
scenario: '停机',
status: '启用'
},
{
id: 2,
ruleName: '非停机服务标准报价规则',
scenario: '非停机',
status: '启用'
},
{
id: 3,
ruleName: '紧急服务加急报价规则',
scenario: '停机',
status: '启用'
}
]
}
},
methods: {
handleAdd() {
window.location.href = '7-报价规则配置新增编辑页.html?action=add';
},
handleEdit(item) {
window.location.href = `7-报价规则配置新增编辑页.html?action=edit&id=${item.id}`;
},
handleToggleStatus(item) {
item.status = item.status === '启用' ? '停用' : '启用';
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>报价规则配置{{ isEdit ? '编辑' : '新增' }} - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 25px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
.form-label .required {
color: #f56c6c;
margin-left: 4px;
}
.form-control {
width: 100%;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #409eff;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 15px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e4e7ed;
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.form-hint {
font-size: 12px;
color: #909399;
margin-top: 5px;
}
.rule-section {
background: #f5f7fa;
padding: 20px;
border-radius: 6px;
margin-bottom: 20px;
}
.rule-section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 15px;
}
.select-multiple {
min-height: 100px;
max-height: 200px;
overflow-y: auto;
border: 1px solid #dcdfe6;
border-radius: 6px;
padding: 10px;
}
.select-item {
padding: 8px;
margin-bottom: 5px;
background: white;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
}
.select-item:hover {
background: #ecf5ff;
}
.select-item.selected {
background: #409eff;
color: white;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<h1>报价规则配置{{ isEdit ? '编辑' : '新增' }}</h1>
</div>
<div class="card">
<div class="card-title">基本信息</div>
<form @submit.prevent="handleSubmit">
<div class="form-group">
<label class="form-label">规则名称 <span class="required">*</span></label>
<input
type="text"
v-model="form.ruleName"
class="form-control"
placeholder="请输入规则名称"
required
/>
</div>
<div class="form-group">
<label class="form-label">适用服务场景 <span class="required">*</span></label>
<select v-model="form.scenario" class="form-control" required>
<option value="">请选择</option>
<option value="停机">停机</option>
<option value="非停机">非停机</option>
</select>
</div>
<!-- 人工费用规则 -->
<div class="rule-section">
<div class="rule-section-title">人工费用规则 <span class="required">*</span></div>
<div class="select-multiple">
<div
v-for="labor in availableLabors"
:key="labor.id"
:class="['select-item', { selected: form.laborRules.includes(labor.id) }]"
@click="toggleLaborRule(labor.id)"
>
{{ labor.serviceType }} - {{ labor.techLevel }} - {{ labor.billingMethod }} (¥{{ labor.price }}/{{ labor.billingMethod === '按小时' ? '小时' : '次' }})
</div>
</div>
<div class="form-hint">点击选择适用的人工费用标准(可多选)</div>
</div>
<!-- 零件费用规则 -->
<div class="rule-section">
<div class="rule-section-title">零件费用规则</div>
<div class="select-multiple">
<div
v-for="part in availableParts"
:key="part.id"
:class="['select-item', { selected: form.partRules.includes(part.id) }]"
@click="togglePartRule(part.id)"
>
{{ part.partName }} ({{ part.specification }}) - ¥{{ part.price }}
</div>
</div>
<div class="form-hint">点击选择适用的零件费用标准(可多选,可选)</div>
</div>
<!-- 加急费规则 -->
<div class="form-row">
<div class="form-group">
<label class="form-label">加急费规则类型</label>
<select v-model="form.urgentFeeType" class="form-control">
<option value="">无加急费</option>
<option value="percentage">百分比</option>
<option value="fixed">固定金额</option>
</select>
</div>
<div class="form-group" v-if="form.urgentFeeType">
<label class="form-label">加急费数值</label>
<input
type="number"
v-model.number="form.urgentFeeValue"
class="form-control"
placeholder="请输入数值"
step="0.01"
min="0"
/>
<div class="form-hint">{{ form.urgentFeeType === 'percentage' ? '单位:%(例如:20表示加收20%)' : '单位:元' }}</div>
</div>
</div>
<!-- 折扣规则 -->
<div class="form-row">
<div class="form-group">
<label class="form-label">折扣规则类型</label>
<select v-model="form.discountType" class="form-control">
<option value="">无折扣</option>
<option value="percentage">百分比折扣</option>
<option value="fixed">固定金额减免</option>
</select>
</div>
<div class="form-group" v-if="form.discountType">
<label class="form-label">折扣数值</label>
<input
type="number"
v-model.number="form.discountValue"
class="form-control"
placeholder="请输入数值"
step="0.01"
min="0"
/>
<div class="form-hint">{{ form.discountType === 'percentage' ? '单位:%(例如:10表示打9折)' : '单位:元' }}</div>
</div>
</div>
<div class="form-group">
<label class="form-label">规则说明</label>
<textarea
v-model="form.description"
class="form-control"
rows="4"
placeholder="请输入规则说明"
></textarea>
</div>
<div class="form-actions">
<button type="button" class="btn btn-default" @click="handleCancel">取消</button>
<button type="submit" class="btn btn-primary">保存</button>
</div>
</form>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
const urlParams = new URLSearchParams(window.location.search);
const action = urlParams.get('action');
return {
isEdit: action === 'edit',
form: {
ruleName: '',
scenario: '',
laborRules: [],
partRules: [],
urgentFeeType: '',
urgentFeeValue: null,
discountType: '',
discountValue: null,
description: ''
},
availableLabors: [
{ id: 1, serviceType: '上门', techLevel: '普通', billingMethod: '按小时', price: 150 },
{ id: 2, serviceType: '上门', techLevel: '高级', billingMethod: '按小时', price: 250 },
{ id: 3, serviceType: '远程', techLevel: '普通', billingMethod: '按次', price: 200 },
{ id: 4, serviceType: '紧急', techLevel: '高级', billingMethod: '按小时', price: 400 }
],
availableParts: [
{ id: 1, partName: '服务器硬盘', specification: '2TB SATA', price: 800 },
{ id: 2, partName: '内存条', specification: '16GB DDR4', price: 600 },
{ id: 3, partName: '网络交换机', specification: '24口千兆', price: 2000 }
]
}
},
mounted() {
if (this.isEdit) {
// 模拟加载数据
this.form = {
ruleName: '停机服务标准报价规则',
scenario: '停机',
laborRules: [1, 2],
partRules: [1, 2],
urgentFeeType: 'percentage',
urgentFeeValue: 20,
discountType: '',
discountValue: null,
description: '适用于停机场景的标准报价规则'
};
}
},
methods: {
toggleLaborRule(id) {
const index = this.form.laborRules.indexOf(id);
if (index > -1) {
this.form.laborRules.splice(index, 1);
} else {
this.form.laborRules.push(id);
}
},
togglePartRule(id) {
const index = this.form.partRules.indexOf(id);
if (index > -1) {
this.form.partRules.splice(index, 1);
} else {
this.form.partRules.push(id);
}
},
handleSubmit() {
if (this.form.laborRules.length === 0) {
alert('请至少选择一个人工费用规则');
return;
}
alert(this.isEdit ? '保存成功!' : '新增成功!');
window.location.href = '6-报价规则配置列表页.html';
},
handleCancel() {
if (confirm('确定要取消吗?未保存的数据将丢失。')) {
window.location.href = '6-报价规则配置列表页.html';
}
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标准报价单生成 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 25px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.form-group {
margin-bottom: 20px;
}
.form-label {
display: block;
margin-bottom: 8px;
color: #333;
font-weight: 500;
font-size: 14px;
}
.form-label .required {
color: #f56c6c;
margin-left: 4px;
}
.form-control {
width: 100%;
padding: 10px 12px;
border: 1px solid #dcdfe6;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.form-control:focus {
outline: none;
border-color: #409eff;
}
.form-control[readonly] {
background: #f5f7fa;
color: #606266;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: 15px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #e4e7ed;
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.quote-detail {
background: #f5f7fa;
padding: 20px;
border-radius: 6px;
margin-top: 20px;
}
.quote-detail-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 15px;
}
.detail-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 15px;
}
.detail-table th,
.detail-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #e4e7ed;
}
.detail-table th {
background: white;
font-weight: 600;
color: #333;
}
.total-row {
font-weight: 600;
font-size: 16px;
color: #409eff;
}
.info-box {
background: #ecf5ff;
border: 1px solid #409eff;
border-radius: 6px;
padding: 15px;
margin-bottom: 20px;
}
.info-box-title {
font-weight: 600;
color: #409eff;
margin-bottom: 8px;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #409eff;
}
input:checked + .slider:before {
transform: translateX(26px);
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<h1>标准报价单生成</h1>
</div>
<div class="card">
<div class="card-title">报价信息</div>
<form @submit.prevent="handleGenerate">
<div class="form-row">
<div class="form-group">
<label class="form-label">客户名称 <span class="required">*</span></label>
<input
type="text"
v-model="form.customerName"
class="form-control"
placeholder="请输入客户名称"
required
/>
</div>
<div class="form-group">
<label class="form-label">是否停机 <span class="required">*</span></label>
<select v-model="form.isDowntime" class="form-control" required @change="updateRule">
<option value="">请选择</option>
<option value="是"></option>
<option value="否"></option>
</select>
</div>
</div>
<div class="form-group">
<label class="form-label">服务内容描述 <span class="required">*</span></label>
<textarea
v-model="form.serviceDescription"
class="form-control"
rows="4"
placeholder="请输入服务内容描述"
required
></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">是否质保期外</label>
<div style="margin-top: 8px;">
<label class="switch">
<input type="checkbox" v-model="form.isOutOfWarranty" checked disabled>
<span class="slider"></span>
</label>
<span style="margin-left: 10px; color: #606266;">是(固定)</span>
</div>
</div>
</div>
<!-- 自动匹配的报价规则 -->
<div class="info-box" v-if="matchedRule">
<div class="info-box-title">匹配的报价规则</div>
<div>规则名称:{{ matchedRule.ruleName }}</div>
<div>适用场景:{{ matchedRule.scenario }}</div>
</div>
<!-- 人工费用明细 -->
<div class="quote-detail" v-if="matchedRule">
<div class="quote-detail-title">人工费用明细(自动计算)</div>
<table class="detail-table">
<thead>
<tr>
<th>服务类型</th>
<th>技术等级</th>
<th>计费方式</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in laborDetails" :key="index">
<td>{{ item.serviceType }}</td>
<td>{{ item.techLevel }}</td>
<td>{{ item.billingMethod }}</td>
<td>
<input
type="number"
v-model.number="item.quantity"
class="form-control"
style="width: 80px; display: inline-block;"
min="0"
step="0.5"
@input="calculateTotal"
/>
<span style="margin-left: 5px;">{{ item.billingMethod === '按小时' ? '小时' : '次' }}</span>
</td>
<td>¥{{ item.price }}/{{ item.billingMethod === '按小时' ? '小时' : '次' }}</td>
<td>¥{{ (item.price * item.quantity).toFixed(2) }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 零件费用明细 -->
<div class="quote-detail">
<div class="quote-detail-title">零件费用明细(可选)</div>
<table class="detail-table">
<thead>
<tr>
<th>零件名称</th>
<th>型号规格</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in partDetails" :key="index">
<td>
<select v-model="item.partId" class="form-control" @change="updatePartInfo(index)">
<option value="">请选择零件</option>
<option v-for="part in availableParts" :key="part.id" :value="part.id">
{{ part.partName }}
</option>
</select>
</td>
<td>{{ item.specification }}</td>
<td>
<input
type="number"
v-model.number="item.quantity"
class="form-control"
style="width: 80px; display: inline-block;"
min="0"
@input="calculateTotal"
/>
</td>
<td>¥{{ item.price }}</td>
<td>¥{{ (item.price * item.quantity).toFixed(2) }}</td>
<td>
<button type="button" class="btn btn-danger btn-small" @click="removePart(index)">删除</button>
</td>
</tr>
<tr>
<td colspan="6">
<button type="button" class="btn btn-primary btn-small" @click="addPart">+ 添加零件</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 总计 -->
<div class="quote-detail" v-if="matchedRule">
<table class="detail-table">
<tbody>
<tr>
<td style="text-align: right; font-weight: 600;">人工费用小计:</td>
<td>¥{{ laborSubtotal.toFixed(2) }}</td>
</tr>
<tr>
<td style="text-align: right; font-weight: 600;">零件费用小计:</td>
<td>¥{{ partSubtotal.toFixed(2) }}</td>
</tr>
<tr class="total-row">
<td style="text-align: right;">总计:</td>
<td>¥{{ totalAmount.toFixed(2) }}</td>
</tr>
</tbody>
</table>
</div>
<div class="form-actions">
<button type="button" class="btn btn-default" @click="handleCancel">取消</button>
<button type="submit" class="btn btn-primary" :disabled="!matchedRule">生成报价单</button>
</div>
</form>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
form: {
customerName: '',
serviceDescription: '',
isDowntime: '',
isOutOfWarranty: true
},
matchedRule: null,
laborDetails: [],
partDetails: [],
availableParts: [
{ id: 1, partName: '服务器硬盘', specification: '2TB SATA', price: 800 },
{ id: 2, partName: '内存条', specification: '16GB DDR4', price: 600 },
{ id: 3, partName: '网络交换机', specification: '24口千兆', price: 2000 }
],
availableRules: [
{
id: 1,
ruleName: '停机服务标准报价规则',
scenario: '停机',
laborRules: [
{ id: 1, serviceType: '上门', techLevel: '普通', billingMethod: '按小时', price: 150 },
{ id: 2, serviceType: '上门', techLevel: '高级', billingMethod: '按小时', price: 250 }
]
},
{
id: 2,
ruleName: '非停机服务标准报价规则',
scenario: '非停机',
laborRules: [
{ id: 3, serviceType: '远程', techLevel: '普通', billingMethod: '按次', price: 200 }
]
}
]
}
},
computed: {
laborSubtotal() {
return this.laborDetails.reduce((sum, item) => sum + (item.price * item.quantity), 0);
},
partSubtotal() {
return this.partDetails.reduce((sum, item) => sum + (item.price * item.quantity), 0);
},
totalAmount() {
return this.laborSubtotal + this.partSubtotal;
}
},
methods: {
updateRule() {
if (this.form.isDowntime) {
const scenario = this.form.isDowntime === '是' ? '停机' : '非停机';
this.matchedRule = this.availableRules.find(r => r.scenario === scenario);
if (this.matchedRule) {
this.laborDetails = this.matchedRule.laborRules.map(rule => ({
...rule,
quantity: 1
}));
}
} else {
this.matchedRule = null;
this.laborDetails = [];
}
this.calculateTotal();
},
updatePartInfo(index) {
const part = this.availableParts.find(p => p.id === this.partDetails[index].partId);
if (part) {
this.partDetails[index].partName = part.partName;
this.partDetails[index].specification = part.specification;
this.partDetails[index].price = part.price;
this.partDetails[index].quantity = 1;
}
this.calculateTotal();
},
addPart() {
this.partDetails.push({
partId: '',
partName: '',
specification: '',
price: 0,
quantity: 0
});
},
removePart(index) {
this.partDetails.splice(index, 1);
this.calculateTotal();
},
calculateTotal() {
// 计算逻辑已在computed中实现
},
handleGenerate() {
if (!this.matchedRule) {
alert('请先选择是否停机,系统将自动匹配报价规则');
return;
}
// 生成报价单编号
const quoteNo = 'QT' + new Date().getTime();
// 跳转到详情页
window.location.href = `9-标准报价单详情页.html?quoteNo=${quoteNo}`;
},
handleCancel() {
if (confirm('确定要取消吗?未保存的数据将丢失。')) {
window.location.href = '1-服务价格标准首页.html';
}
}
}
}).mount('#app');
</script>
</body>
</html>
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>标准报价单详情 - 服务价格标准化管理系统</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f7fa;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
background: white;
padding: 20px 30px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.header-top {
display: flex;
justify-content: space-between;
align-items: center;
}
.header h1 {
color: #333;
font-size: 24px;
font-weight: 600;
}
.header-actions {
display: flex;
gap: 10px;
}
.card {
background: white;
border-radius: 8px;
padding: 30px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
margin-bottom: 25px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.quote-header {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
margin-bottom: 30px;
}
.quote-info-item {
margin-bottom: 15px;
}
.quote-info-label {
font-size: 14px;
color: #909399;
margin-bottom: 5px;
}
.quote-info-value {
font-size: 16px;
color: #333;
font-weight: 500;
}
.quote-no {
font-size: 20px;
color: #409eff;
font-weight: 600;
}
.detail-section {
margin-bottom: 30px;
}
.detail-section-title {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 15px;
padding-bottom: 8px;
border-bottom: 1px solid #e4e7ed;
}
.detail-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 20px;
}
.detail-table th,
.detail-table td {
padding: 12px;
text-align: left;
border-bottom: 1px solid #e4e7ed;
}
.detail-table th {
background: #f5f7fa;
font-weight: 600;
color: #333;
}
.detail-table tr:last-child td {
border-bottom: none;
}
.summary-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.summary-table td {
padding: 12px;
text-align: right;
}
.summary-table td:first-child {
text-align: left;
font-weight: 600;
color: #333;
}
.total-row {
font-weight: 600;
font-size: 18px;
color: #409eff;
border-top: 2px solid #409eff;
}
.total-row td {
padding-top: 15px;
padding-bottom: 15px;
}
.quote-footer {
background: #f5f7fa;
padding: 20px;
border-radius: 6px;
margin-top: 30px;
}
.quote-footer-item {
margin-bottom: 10px;
font-size: 14px;
color: #606266;
}
.quote-footer-item:last-child {
margin-bottom: 0;
}
.quote-footer-label {
font-weight: 600;
color: #333;
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: all 0.3s;
font-weight: 500;
}
.btn-primary {
background: #409eff;
color: white;
}
.btn-primary:hover {
background: #66b1ff;
}
.btn-success {
background: #67c23a;
color: white;
}
.btn-success:hover {
background: #85ce61;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status-badge.active {
background: #f0f9ff;
color: #67c23a;
border: 1px solid #67c23a;
}
@media print {
body {
background: white;
padding: 0;
}
.header-actions {
display: none;
}
.card {
box-shadow: none;
}
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<div class="header">
<div class="header-top">
<h1>标准报价单详情</h1>
<div class="header-actions">
<button class="btn btn-success" @click="handleExport">导出</button>
<button class="btn btn-primary" @click="handlePrint">打印</button>
<button class="btn btn-default" @click="handleBack">返回</button>
</div>
</div>
</div>
<div class="card">
<!-- 报价单头部信息 -->
<div class="quote-header">
<div>
<div class="quote-info-item">
<div class="quote-info-label">报价单编号</div>
<div class="quote-info-value quote-no">{{ quoteInfo.quoteNo }}</div>
</div>
<div class="quote-info-item">
<div class="quote-info-label">客户名称</div>
<div class="quote-info-value">{{ quoteInfo.customerName }}</div>
</div>
<div class="quote-info-item">
<div class="quote-info-label">服务内容</div>
<div class="quote-info-value">{{ quoteInfo.serviceDescription }}</div>
</div>
</div>
<div>
<div class="quote-info-item">
<div class="quote-info-label">报价日期</div>
<div class="quote-info-value">{{ quoteInfo.quoteDate }}</div>
</div>
<div class="quote-info-item">
<div class="quote-info-label">报价有效期</div>
<div class="quote-info-value">{{ quoteInfo.validUntil }}</div>
</div>
<div class="quote-info-item">
<div class="quote-info-label">状态</div>
<div class="quote-info-value">
<span class="status-badge active">{{ quoteInfo.status }}</span>
</div>
</div>
</div>
</div>
<!-- 人工费用明细 -->
<div class="detail-section" v-if="laborDetails.length > 0">
<div class="detail-section-title">人工费用明细</div>
<table class="detail-table">
<thead>
<tr>
<th>序号</th>
<th>服务类型</th>
<th>技术等级</th>
<th>计费方式</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in laborDetails" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item.serviceType }}</td>
<td>{{ item.techLevel }}</td>
<td>{{ item.billingMethod }}</td>
<td>{{ item.quantity }} {{ item.billingMethod === '按小时' ? '小时' : '次' }}</td>
<td>¥{{ item.price }}/{{ item.billingMethod === '按小时' ? '小时' : '次' }}</td>
<td>¥{{ (item.price * item.quantity).toFixed(2) }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 零件费用明细 -->
<div class="detail-section" v-if="partDetails.length > 0">
<div class="detail-section-title">零件费用明细</div>
<table class="detail-table">
<thead>
<tr>
<th>序号</th>
<th>零件名称</th>
<th>型号规格</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in partDetails" :key="index">
<td>{{ index + 1 }}</td>
<td>{{ item.partName }}</td>
<td>{{ item.specification }}</td>
<td>{{ item.quantity }}</td>
<td>¥{{ item.price }}</td>
<td>¥{{ (item.price * item.quantity).toFixed(2) }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 费用汇总 -->
<div class="detail-section">
<div class="detail-section-title">费用汇总</div>
<table class="summary-table">
<tbody>
<tr v-if="laborSubtotal > 0">
<td>人工费用小计:</td>
<td>¥{{ laborSubtotal.toFixed(2) }}</td>
</tr>
<tr v-if="partSubtotal > 0">
<td>零件费用小计:</td>
<td>¥{{ partSubtotal.toFixed(2) }}</td>
</tr>
<tr class="total-row">
<td>总计(含税):</td>
<td>¥{{ totalAmount.toFixed(2) }}</td>
</tr>
</tbody>
</table>
</div>
<!-- 报价依据 -->
<div class="quote-footer">
<div class="quote-footer-item">
<span class="quote-footer-label">报价依据:</span>
服务价格白皮书 {{ quoteInfo.whitebookVersion }}
</div>
<div class="quote-footer-item">
<span class="quote-footer-label">适用规则:</span>
{{ quoteInfo.ruleName }}
</div>
<div class="quote-footer-item">
<span class="quote-footer-label">服务场景:</span>
{{ quoteInfo.scenario }}(质保期外)
</div>
<div class="quote-footer-item">
<span class="quote-footer-label">备注:</span>
{{ quoteInfo.remark || '无' }}
</div>
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
// 从URL获取报价单编号
const urlParams = new URLSearchParams(window.location.search);
const quoteNo = urlParams.get('quoteNo') || 'QT' + new Date().getTime();
// 模拟报价单数据(实际应从后端获取)
return {
quoteInfo: {
quoteNo: quoteNo,
customerName: '示例客户有限公司',
serviceDescription: '服务器故障维修服务,包括硬件检测、故障排查、零件更换等',
quoteDate: new Date().toLocaleDateString('zh-CN'),
validUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toLocaleDateString('zh-CN'),
status: '有效',
whitebookVersion: 'V1.0',
ruleName: '停机服务标准报价规则',
scenario: '停机',
remark: '本报价单有效期30天,最终价格以实际服务为准。'
},
laborDetails: [
{
serviceType: '上门',
techLevel: '普通',
billingMethod: '按小时',
price: 150,
quantity: 2
},
{
serviceType: '上门',
techLevel: '高级',
billingMethod: '按小时',
price: 250,
quantity: 1
}
],
partDetails: [
{
partName: '服务器硬盘',
specification: '2TB SATA',
price: 800,
quantity: 1
},
{
partName: '内存条',
specification: '16GB DDR4',
price: 600,
quantity: 2
}
]
}
},
computed: {
laborSubtotal() {
return this.laborDetails.reduce((sum, item) => sum + (item.price * item.quantity), 0);
},
partSubtotal() {
return this.partDetails.reduce((sum, item) => sum + (item.price * item.quantity), 0);
},
totalAmount() {
return this.laborSubtotal + this.partSubtotal;
}
},
methods: {
handleExport() {
alert('导出功能(占位)\n将导出为PDF或Excel格式');
},
handlePrint() {
window.print();
},
handleBack() {
window.location.href = '8-标准报价单生成页.html';
}
}
}).mount('#app');
</script>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment