Commit 01256ea8 by suyuchen

1master

parent bd3d2395
<!DOCTYPE html> ++ /dev/null
<!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> ++ /dev/null
<!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>
...@@ -965,3 +965,4 @@ ...@@ -965,3 +965,4 @@
...@@ -18,16 +18,16 @@ ...@@ -18,16 +18,16 @@
</svg> </svg>
</div> </div>
<div class="nav-submenu"> <div class="nav-submenu">
<a href="二维码列表页.html" class="nav-subitem "> <a href="人工费用标准管理列表页.html" class="nav-subitem ">
人工管理 人工管理
</a> </a>
<a href="设备档案1.html" class="nav-subitem "> <a href="/1,2,3/2服务价格标准化/4-零件费用标准管理列表页.html" class="nav-subitem ">
零件管理 零件管理
</a> </a>
<a href="设备机型.html" class="nav-subitem "> <a href="标准报价单生成.html" class="nav-subitem ">
报价管理 报价管理
</a> </a>
<a href="设备机型.html" class="nav-subitem "> <a href="服务单列表.html" class="nav-subitem ">
服务单列表 服务单列表
</a> </a>
</div> </div>
...@@ -195,6 +195,9 @@ ...@@ -195,6 +195,9 @@
<a href="客户公司管理1.html" class="nav-subitem"> <a href="客户公司管理1.html" class="nav-subitem">
客户公司管理 客户公司管理
</a> </a>
<a href="信息原页面.html" class="nav-subitem">
信息原页面
</a>
</div> </div>
<!-- 客户账号管理 --> <!-- 客户账号管理 -->
......
async function loadSidebar() { async function loadSidebar() {
try { try {
const response = await fetch('aside.html'); // 根据当前页面路径计算aside.html的相对路径
const currentPath = window.location.pathname;
// 计算目录深度:分割路径,过滤掉空字符串和文件名
const pathParts = currentPath.split('/').filter(part => part && !part.endsWith('.html'));
let asidePath = 'aside.html';
// 如果页面在子目录中,需要向上查找
if (pathParts.length > 0) {
const upLevels = '../'.repeat(pathParts.length);
asidePath = upLevels + 'aside.html';
}
const response = await fetch(asidePath);
const html = await response.text(); const html = await response.text();
document.getElementById('sidebar-container').innerHTML = html; document.getElementById('sidebar-container').innerHTML = html;
} catch (error) { } catch (error) {
......
<!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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
:root {
--primary-color: #1B64F3;
--primary-hover: #1554D3;
--primary-light: #e6f7ff;
--bg-color: #F5F6FA;
--card-bg: #FFFFFF;
--sidebar-bg: #0F172A;
--text-primary: #1A1F36;
--text-secondary: #64748B;
--text-tertiary: #94A3B8;
--text-light: #CBD5E1;
--border-color: #E2E8F0;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--success-color: #22C55E;
--warning-color: #EAB308;
--error-color: #EF4444;
--sidebar-width: 260px;
--header-height: 64px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 左侧导航栏 */
.sidebar {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 1000;
}
.sidebar-logo {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
}
.logo-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.sidebar-menu {
padding: 16px 0;
}
.nav-group-title {
padding: 12px 20px 8px;
font-size: 11px;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.menu-item, .nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-light);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
position: relative;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.menu-item:hover, .nav-item:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.menu-item.active, .nav-item.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary-color);
}
.nav-item.has-submenu {
justify-content: space-between;
}
.nav-arrow {
width: 14px;
height: 14px;
transition: transform 0.2s;
margin-left: auto;
}
.nav-item.expanded .nav-arrow {
transform: rotate(90deg);
}
.nav-submenu {
display: none;
background: rgba(0, 0, 0, 0.1);
}
.nav-item.expanded + .nav-submenu {
display: block;
}
.nav-subitem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px 10px 48px;
color: var(--text-light);
text-decoration: none;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
position: relative;
}
.nav-subitem:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.nav-subitem.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-icon {
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* 顶部工具栏 */
.header, .top-header {
position: fixed;
top: 0;
left: var(--sidebar-width);
right: 0;
height: var(--header-height);
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
z-index: 999;
box-shadow: var(--shadow-sm);
}
.header-left {
display: flex;
align-items: center;
gap: 24px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.breadcrumb-separator {
color: var(--text-tertiary);
}
.breadcrumb-current {
color: var(--text-primary);
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.header-icon-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 8px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.notification-badge {
position: absolute;
top: 6px;
right: 6px;
width: 8px;
height: 8px;
background: var(--error-color);
border-radius: 50%;
border: 2px solid var(--card-bg);
}
.lang-switcher {
display: flex;
gap: 6px;
align-items: center;
}
.lang-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.lang-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.lang-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
cursor: pointer;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
.user-info {
display: flex;
align-items: center;
gap: 16px;
}
.notification {
position: relative;
cursor: pointer;
color: var(--text-secondary);
}
/* 主内容区域 - 已调整左右内边距 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
min-height: 100vh;
margin-left: 260px;
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
/* 调整左右内边距,从 8px 增加到 32px,以利用更多空间 */
/* padding: 4px 32px;
min-height: calc(100vh - var(--header-height));
.main-content { } */
}
.content-area {
padding: 0;
}
/* 页面标题区域 */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.page-title-main {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.page-title-sub {
font-size: 14px;
color: var(--text-secondary);
}
/* 表格数据优化 */
.table-cell-number {
font-weight: 600;
color: var(--text-primary);
}
.table-cell-date {
color: var(--text-secondary);
font-size: 13px;
}
/* 表格内容样式增强 */
td strong {
color: var(--text-primary);
font-weight: 600;
}
td code {
font-size: 12px;
font-weight: 500;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 48px;
color: var(--text-tertiary);
margin-bottom: 16px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
}
/* 响应式表格优化 */
@media (max-width: 1200px) {
.table-container {
overflow-x: auto;
}
table {
min-width: 1000px;
}
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
th, td {
padding: 12px 16px;
}
.action-buttons {
flex-direction: column;
gap: 6px;
}
.action-btn {
width: 100%;
justify-content: center;
}
}
/* 卡片样式 */
.card {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 24px;
border: 1px solid var(--border-color);
}
.card-header {
padding: 18px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-color);
}
.card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.2px;
}
.card-body {
padding: 20px 16px;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(27, 100, 243, 0.3);
}
.btn-secondary {
background: #f0f0f0;
color: var(--text-light);
}
.btn-secondary:hover {
background: #e0e0e0;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-success:hover {
background: #389e0d;
}
.btn-danger {
background: var(--error-color);
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
/* 标签页样式 */
.tabs {
display: flex;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 10px 0;
color: var(--text-light);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-size: 14px;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
/* 表格样式 */
.table-container {
overflow-x: auto;
border-radius: 8px;
width: 100%;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
}
th, td {
padding: 16px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
tbody tr {
transition: all 0.2s;
}
tbody tr:hover {
background: rgba(27, 100, 243, 0.04);
}
tbody tr:last-child td {
border-bottom: none;
}
td {
color: var(--text-primary);
font-size: 14px;
vertical-align: middle;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status.active {
background: #f6ffed;
color: var(--success-color);
}
.status.inactive {
background: #fff2e8;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.action-btn {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.action-btn i {
font-size: 12px;
}
.action-btn.edit-btn {
background: rgba(27, 100, 243, 0.1);
color: var(--primary-color);
}
.action-btn.edit-btn:hover {
background: rgba(27, 100, 243, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.delete-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--error-color);
}
.action-btn.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}
.action-btn.view-btn {
background: rgba(27, 100, 243, 0.08);
color: var(--primary-color);
}
.action-btn.view-btn:hover {
background: rgba(27, 100, 243, 0.15);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.copy-btn {
background: rgba(34, 197, 94, 0.1);
color: var(--success-color);
}
.action-btn.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.add-modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 800px;
max-height: 85vh;
overflow-y: auto;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
}
.close-modal {
background: none;
border: none;
font-size: 20px;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.3s;
}
.close-modal:hover {
background: var(--secondary-color);
color: var(--text-color);
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
/* 新增表单模态框样式 */
.add-modal-form .form-group {
margin-bottom: 20px;
}
.add-modal-form .form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.add-modal-form .form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.add-modal-form label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--text-secondary);
font-size: 14px;
}
.add-modal-form input,
.add-modal-form select,
.add-modal-form textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.add-modal-form input:focus,
.add-modal-form select:focus,
.add-modal-form textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.add-modal-form .section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.upload-box {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: var(--bg-color);
}
.upload-box:hover {
border-color: var(--primary-color);
background-color: rgba(27, 100, 243, 0.05);
}
.upload-icon {
font-size: 24px;
color: var(--primary-color);
margin-bottom: 10px;
}
.upload-info {
color: var(--text-secondary);
font-size: 13px;
}
.file-name {
margin-top: 10px;
font-weight: 500;
color: var(--primary-color);
font-size: 13px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.param-table th, .param-table td {
border: 1px solid var(--border-color);
padding: 8px;
text-align: left;
}
.param-table th {
background-color: var(--bg-color);
font-weight: 600;
}
.add-param-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
font-size: 14px;
}
.add-param-btn:hover {
background: var(--primary-hover);
}
/* 二维码预览样式 */
.qrcode-preview-large {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: var(--primary-light);
border-radius: 8px;
margin-bottom: 20px;
}
.qrcode-image {
width: 200px;
height: 200px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.qrcode-image img {
max-width: 100%;
max-height: 100%;
}
/* 详情信息样式 */
.detail-info {
margin-top: 20px;
}
.detail-item {
display: flex;
margin-bottom: 12px;
font-size: 14px;
}
.detail-label {
width: 100px;
color: var(--text-light);
flex-shrink: 0;
}
.detail-value {
color: var(--text-color);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show-mobile {
transform: translateX(0);
}
.main-content {
margin-left: 0;
margin-top: 0;
padding: 16px; /* 在移动端保持较小的内边距 */
}
.header, .top-header {
left: 0;
}
.tabs {
flex-wrap: wrap;
}
}
/* 批量上传相关样式 */
.batch-upload-modal .modal-content {
max-width: 800px;
}
.template-selector {
margin-bottom: 20px;
}
.template-selector label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.template-selector select {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: white;
}
.template-download-btn {
margin-top: 10px;
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.template-download-btn:hover {
background: var(--primary-hover);
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s;
background: var(--bg-color);
}
.upload-area:hover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.upload-area.dragover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.1);
}
.upload-icon-large {
font-size: 48px;
color: var(--primary-color);
margin-bottom: 12px;
}
.upload-text {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.upload-hint {
font-size: 12px;
color: var(--text-tertiary);
}
.progress-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.progress-stats {
display: flex;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
}
.stat-label {
color: var(--text-secondary);
}
.stat-value {
font-weight: 600;
color: var(--text-primary);
}
.stat-value.success {
color: var(--success-color);
}
.stat-value.error {
color: var(--error-color);
}
.progress-bar-container {
width: 100%;
height: 8px;
background: var(--bg-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
transition: width 0.3s ease;
border-radius: 4px;
}
.error-list {
margin-top: 16px;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px;
background: #fff5f5;
}
.error-item {
padding: 8px;
margin-bottom: 6px;
background: white;
border-left: 3px solid var(--error-color);
border-radius: 4px;
font-size: 13px;
}
.error-item:last-child {
margin-bottom: 0;
}
.error-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.error-reason {
color: var(--error-color);
font-weight: 500;
}
.download-error-btn {
margin-top: 12px;
padding: 8px 16px;
background: var(--error-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.download-error-btn:hover {
background: #cf1322;
}
.validation-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-size: 13px;
table-layout: fixed;
}
.validation-table th,
.validation-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: normal;
word-break: normal;
overflow: visible;
vertical-align: middle;
}
.validation-table th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.validation-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.validation-table td:nth-child(5) {
white-space: normal;
word-break: break-word;
}
.validation-table th:nth-child(1),
.validation-table td:nth-child(1) {
width: 60px;
min-width: 60px;
text-align: center;
}
.validation-table th:nth-child(2),
.validation-table td:nth-child(2) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(3),
.validation-table td:nth-child(3) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(4),
.validation-table td:nth-child(4) {
width: 100px;
min-width: 100px;
text-align: center;
}
.validation-table th:nth-child(5),
.validation-table td:nth-child(5) {
width: auto;
min-width: 200px;
}
.validation-table tr.error-row {
background: #fff5f5;
}
.validation-table tr.error-row td {
color: var(--error-color);
}
#validation-table-container {
overflow-x: auto;
max-width: 100%;
width: 100%;
}
#validation-table-container table {
min-width: 600px;
}
.file-name-display {
margin-top: 12px;
padding: 10px;
background: var(--bg-color);
border-radius: 6px;
font-size: 13px;
color: var(--text-secondary);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #01337a;
color: #fff;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
td {
padding: 10px;
border: 1px solid #ddd;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 在主页面CSS中添加 */
.sidebar-iframe {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
border: none;
z-index: 999;
}
/* 为main-content和header添加左边距以避开iframe */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
}
.top-header {
left: var(--sidebar-width);
}
</style>
<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 class="app-container">
<div id="sidebar-container"></div>
<header class="top-header">
<div class="header-left">
<div class="breadcrumb">
<span>首页</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">人工费用</span>
</div>
</div>
<div class="header-right">
<button class="header-icon-btn" title="通知">
<svg class="nav-icon" viewBox="0 0 20 20">
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
</svg>
<span class="notification-badge"></span>
</button>
<div class="lang-switcher">
<button class="lang-btn active">中文</button>
<button class="lang-btn">EN</button>
</div>
<div class="user-avatar"></div>
</div>
</header>
<div class="main-content" style="position: relative;">
<div id="app">
<div class="container">
<div class="card" style="padding: 20px;">
<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 = '人工费用标准管理列表页.html';
},
handleCancel() {
if (confirm('确定要取消吗?未保存的数据将丢失。')) {
window.location.href = '人工费用标准管理列表页.html';
}
}
}
}).mount('#app');
</script>
</div>
</div>
</body>
<script src="./js.js"></script>
</html>
\ No newline at end of file
<!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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #1B64F3;
--primary-hover: #1554D3;
--primary-light: #e6f7ff;
--bg-color: #F5F6FA;
--card-bg: #FFFFFF;
--sidebar-bg: #0F172A;
--text-primary: #1A1F36;
--text-secondary: #64748B;
--text-tertiary: #94A3B8;
--text-light: #CBD5E1;
--border-color: #E2E8F0;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--success-color: #22C55E;
--warning-color: #EAB308;
--error-color: #EF4444;
--sidebar-width: 260px;
--header-height: 64px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 左侧导航栏 */
.sidebar {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 1000;
}
.sidebar-logo {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
}
.logo-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.sidebar-menu {
padding: 16px 0;
}
.nav-group-title {
padding: 12px 20px 8px;
font-size: 11px;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.menu-item, .nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-light);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
position: relative;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.menu-item:hover, .nav-item:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.menu-item.active, .nav-item.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary-color);
}
.nav-item.has-submenu {
justify-content: space-between;
}
.nav-arrow {
width: 14px;
height: 14px;
transition: transform 0.2s;
margin-left: auto;
}
.nav-item.expanded .nav-arrow {
transform: rotate(90deg);
}
.nav-submenu {
display: none;
background: rgba(0, 0, 0, 0.1);
}
.nav-item.expanded + .nav-submenu {
display: block;
}
.nav-subitem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px 10px 48px;
color: var(--text-light);
text-decoration: none;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
position: relative;
}
.nav-subitem:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.nav-subitem.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-icon {
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* 顶部工具栏 */
.header, .top-header {
position: fixed;
top: 0;
left: var(--sidebar-width);
right: 0;
height: var(--header-height);
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
z-index: 999;
box-shadow: var(--shadow-sm);
}
.header-left {
display: flex;
align-items: center;
gap: 24px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.breadcrumb-separator {
color: var(--text-tertiary);
}
.breadcrumb-current {
color: var(--text-primary);
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.header-icon-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 8px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.notification-badge {
position: absolute;
top: 6px;
right: 6px;
width: 8px;
height: 8px;
background: var(--error-color);
border-radius: 50%;
border: 2px solid var(--card-bg);
}
.lang-switcher {
display: flex;
gap: 6px;
align-items: center;
}
.lang-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.lang-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.lang-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
cursor: pointer;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
.user-info {
display: flex;
align-items: center;
gap: 16px;
}
.notification {
position: relative;
cursor: pointer;
color: var(--text-secondary);
}
/* 主内容区域 - 已调整左右内边距 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
min-height: 100vh;
margin-left: 260px;
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
/* 调整左右内边距,从 8px 增加到 32px,以利用更多空间 */
/* padding: 4px 32px;
min-height: calc(100vh - var(--header-height));
.main-content { } */
}
.content-area {
padding: 0;
}
/* 页面标题区域 */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.page-title-main {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.page-title-sub {
font-size: 14px;
color: var(--text-secondary);
}
/* 表格数据优化 */
.table-cell-number {
font-weight: 600;
color: var(--text-primary);
}
.table-cell-date {
color: var(--text-secondary);
font-size: 13px;
}
/* 表格内容样式增强 */
td strong {
color: var(--text-primary);
font-weight: 600;
}
td code {
font-size: 12px;
font-weight: 500;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 48px;
color: var(--text-tertiary);
margin-bottom: 16px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
}
/* 响应式表格优化 */
@media (max-width: 1200px) {
.table-container {
overflow-x: auto;
}
table {
min-width: 1000px;
}
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
th, td {
padding: 12px 16px;
}
.action-buttons {
flex-direction: column;
gap: 6px;
}
.action-btn {
width: 100%;
justify-content: center;
}
}
/* 卡片样式 */
.card {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 24px;
border: 1px solid var(--border-color);
}
.card-header {
padding: 18px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-color);
}
.card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.2px;
}
.card-body {
padding: 20px 16px;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(27, 100, 243, 0.3);
}
.btn-secondary {
background: #f0f0f0;
color: var(--text-light);
}
.btn-secondary:hover {
background: #e0e0e0;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-success:hover {
background: #389e0d;
}
.btn-danger {
background: var(--error-color);
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
/* 标签页样式 */
.tabs {
display: flex;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 10px 0;
color: var(--text-light);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-size: 14px;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
/* 表格样式 */
.table-container {
overflow-x: auto;
border-radius: 8px;
width: 100%;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
}
th, td {
padding: 16px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
tbody tr {
transition: all 0.2s;
}
tbody tr:hover {
background: rgba(27, 100, 243, 0.04);
}
tbody tr:last-child td {
border-bottom: none;
}
td {
color: var(--text-primary);
font-size: 14px;
vertical-align: middle;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status.active {
background: #f6ffed;
color: var(--success-color);
}
.status.inactive {
background: #fff2e8;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.action-btn {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.action-btn i {
font-size: 12px;
}
.action-btn.edit-btn {
background: rgba(27, 100, 243, 0.1);
color: var(--primary-color);
}
.action-btn.edit-btn:hover {
background: rgba(27, 100, 243, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.delete-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--error-color);
}
.action-btn.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}
.action-btn.view-btn {
background: rgba(27, 100, 243, 0.08);
color: var(--primary-color);
}
.action-btn.view-btn:hover {
background: rgba(27, 100, 243, 0.15);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.copy-btn {
background: rgba(34, 197, 94, 0.1);
color: var(--success-color);
}
.action-btn.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.add-modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 800px;
max-height: 85vh;
overflow-y: auto;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
}
.close-modal {
background: none;
border: none;
font-size: 20px;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.3s;
}
.close-modal:hover {
background: var(--secondary-color);
color: var(--text-color);
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
/* 新增表单模态框样式 */
.add-modal-form .form-group {
margin-bottom: 20px;
}
.add-modal-form .form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.add-modal-form .form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.add-modal-form label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--text-secondary);
font-size: 14px;
}
.add-modal-form input,
.add-modal-form select,
.add-modal-form textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.add-modal-form input:focus,
.add-modal-form select:focus,
.add-modal-form textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.add-modal-form .section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.upload-box {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: var(--bg-color);
}
.upload-box:hover {
border-color: var(--primary-color);
background-color: rgba(27, 100, 243, 0.05);
}
.upload-icon {
font-size: 24px;
color: var(--primary-color);
margin-bottom: 10px;
}
.upload-info {
color: var(--text-secondary);
font-size: 13px;
}
.file-name {
margin-top: 10px;
font-weight: 500;
color: var(--primary-color);
font-size: 13px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.param-table th, .param-table td {
border: 1px solid var(--border-color);
padding: 8px;
text-align: left;
}
.param-table th {
background-color: var(--bg-color);
font-weight: 600;
}
.add-param-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
font-size: 14px;
}
.add-param-btn:hover {
background: var(--primary-hover);
}
/* 二维码预览样式 */
.qrcode-preview-large {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: var(--primary-light);
border-radius: 8px;
margin-bottom: 20px;
}
.qrcode-image {
width: 200px;
height: 200px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.qrcode-image img {
max-width: 100%;
max-height: 100%;
}
/* 详情信息样式 */
.detail-info {
margin-top: 20px;
}
.detail-item {
display: flex;
margin-bottom: 12px;
font-size: 14px;
}
.detail-label {
width: 100px;
color: var(--text-light);
flex-shrink: 0;
}
.detail-value {
color: var(--text-color);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show-mobile {
transform: translateX(0);
}
.main-content {
margin-left: 0;
margin-top: 0;
padding: 16px; /* 在移动端保持较小的内边距 */
}
.header, .top-header {
left: 0;
}
.tabs {
flex-wrap: wrap;
}
}
/* 批量上传相关样式 */
.batch-upload-modal .modal-content {
max-width: 800px;
}
.template-selector {
margin-bottom: 20px;
}
.template-selector label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.template-selector select {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: white;
}
.template-download-btn {
margin-top: 10px;
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.template-download-btn:hover {
background: var(--primary-hover);
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s;
background: var(--bg-color);
}
.upload-area:hover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.upload-area.dragover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.1);
}
.upload-icon-large {
font-size: 48px;
color: var(--primary-color);
margin-bottom: 12px;
}
.upload-text {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.upload-hint {
font-size: 12px;
color: var(--text-tertiary);
}
.progress-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.progress-stats {
display: flex;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
}
.stat-label {
color: var(--text-secondary);
}
.stat-value {
font-weight: 600;
color: var(--text-primary);
}
.stat-value.success {
color: var(--success-color);
}
.stat-value.error {
color: var(--error-color);
}
.progress-bar-container {
width: 100%;
height: 8px;
background: var(--bg-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
transition: width 0.3s ease;
border-radius: 4px;
}
.error-list {
margin-top: 16px;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px;
background: #fff5f5;
}
.error-item {
padding: 8px;
margin-bottom: 6px;
background: white;
border-left: 3px solid var(--error-color);
border-radius: 4px;
font-size: 13px;
}
.error-item:last-child {
margin-bottom: 0;
}
.error-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.error-reason {
color: var(--error-color);
font-weight: 500;
}
.download-error-btn {
margin-top: 12px;
padding: 8px 16px;
background: var(--error-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.download-error-btn:hover {
background: #cf1322;
}
.validation-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-size: 13px;
table-layout: fixed;
}
.validation-table th,
.validation-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: normal;
word-break: normal;
overflow: visible;
vertical-align: middle;
}
.validation-table th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.validation-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.validation-table td:nth-child(5) {
white-space: normal;
word-break: break-word;
}
.validation-table th:nth-child(1),
.validation-table td:nth-child(1) {
width: 60px;
min-width: 60px;
text-align: center;
}
.validation-table th:nth-child(2),
.validation-table td:nth-child(2) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(3),
.validation-table td:nth-child(3) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(4),
.validation-table td:nth-child(4) {
width: 100px;
min-width: 100px;
text-align: center;
}
.validation-table th:nth-child(5),
.validation-table td:nth-child(5) {
width: auto;
min-width: 200px;
}
.validation-table tr.error-row {
background: #fff5f5;
}
.validation-table tr.error-row td {
color: var(--error-color);
}
#validation-table-container {
overflow-x: auto;
max-width: 100%;
width: 100%;
}
#validation-table-container table {
min-width: 600px;
}
.file-name-display {
margin-top: 12px;
padding: 10px;
background: var(--bg-color);
border-radius: 6px;
font-size: 13px;
color: var(--text-secondary);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #01337a;
color: #fff;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
td {
padding: 10px;
border: 1px solid #ddd;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 在主页面CSS中添加 */
.sidebar-iframe {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
border: none;
z-index: 999;
}
/* 为main-content和header添加左边距以避开iframe */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
}
.top-header {
left: var(--sidebar-width);
}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div class="app-container">
<div id="sidebar-container"></div>
<header class="top-header">
<div class="header-left">
<div class="breadcrumb">
<span>首页</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">人工费用</span>
</div>
</div>
<div class="header-right">
<button class="header-icon-btn" title="通知">
<svg class="nav-icon" viewBox="0 0 20 20">
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
</svg>
<span class="notification-badge"></span>
</button>
<div class="lang-switcher">
<button class="lang-btn active">中文</button>
<button class="lang-btn">EN</button>
</div>
<div class="user-avatar"></div>
</div>
</header>
<div class="main-content" style="position: relative;">
<div id="app">
<div class="container">
<div class="card" style="padding: 20px;">
<div class="toolbar" style="display:flex;justify-content: end;margin-bottom: 20px;">
<button class="btn btn-primary" style="text-align: right;" @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>-->
<svg t="1767688375858" class="icon" viewBox="0 0 1024 1024" version="1.1"
@click="handleEdit(item)" style="cursor: pointer;margin-right: 5px;"
xmlns="http://www.w3.org/2000/svg" p-id="4934" width="20" height="20">
<path d="M800.8 956.7H232.7c-89.3 0-162-72.7-162-162V226.6c0-89.3 72.7-162 162-162h218.5c17.7 0 32 14.3 32 32s-14.3 32-32 32H232.7c-54 0-98 44-98 98v568.1c0 54 44 98 98 98h568.1c54 0 98-44 98-98V586.6c0-17.7 14.3-32 32-32s32 14.3 32 32v208.1c0 89.3-72.7 162-162 162z"
p-id="4935"></path>
<path d="M525 669.4c-8.2 0-16.4-3.1-22.6-9.4L368 525.6c-12.5-12.5-12.5-32.8 0-45.3L756.2 92.2c8.7-8.7 33-29 69.9-29 26.5 0 51.4 10.3 70 29l39.8 39.8c38.6 38.6 38.6 101.3 0 139.9L547.6 660c-6.2 6.3-14.4 9.4-22.6 9.4zM435.9 503l89.1 89.1 365.6-365.6c13.6-13.6 13.6-35.7 0-49.3l-39.8-39.8-0.1-0.1c-6.5-6.5-15.2-10.1-24.6-10.1-13.4 0-22.3 7.8-24.7 10.2L435.9 503z"
p-id="4936"></path>
<path d="M350.8 709.2c-8.4 0-16.5-3.3-22.6-9.4-8.1-8.1-11.2-19.9-8.3-30.9L366.2 496c3-11 11.6-19.7 22.6-22.6 11-3 22.8 0.2 30.9 8.3l126.6 126.6c8.1 8.1 11.2 19.9 8.3 30.9-3 11-11.6 19.7-22.6 22.6l-172.9 46.3c-2.8 0.7-5.5 1.1-8.3 1.1z m62.9-143.1L396 632l65.8-17.6-48.1-48.3zM848 316c-8.3 0-16.6-3.2-22.8-9.6L707 186.1c-12.4-12.6-12.2-32.9 0.4-45.3 12.6-12.4 32.9-12.2 45.3 0.4l118.2 120.3c12.4 12.6 12.2 32.9-0.4 45.3-6.3 6.1-14.4 9.2-22.5 9.2z"
p-id="4937"></path>
</svg>
<!--<button
v-if="item.status === '启用'"
class="btn btn-warning btn-small"
@click="handleToggleStatus(item)">
停用
</button>-->
<svg t="1767688450917" class="icon" viewBox="0 0 1024 1024" version="1.1"
v-if="item.status === '启用'" @click="handleToggleStatus(item)" style="cursor: pointer;"
xmlns="http://www.w3.org/2000/svg" p-id="5940" width="20" height="20">
<path d="M512 938.688A426.688 426.688 0 1 1 512 85.376a426.688 426.688 0 0 1 0 853.312z m0-85.376A341.312 341.312 0 1 0 512 170.688a341.312 341.312 0 0 0 0 682.624z m208.64-489.664l-356.992 357.056a257.728 257.728 0 0 1-60.352-60.352l357.056-357.056c23.296 16.64 43.712 37.056 60.352 60.352z"
fill="#909399" p-id="5941"></path>
</svg>
<!--<button
v-else
class="btn btn-success btn-small"
@click="handleToggleStatus(item)">
启用
</button>-->
<svg t="1767688500016" class="icon" viewBox="0 0 1024 1024" version="1.1" v-else @click="handleToggleStatus(item)" style="cursor: pointer;"
xmlns="http://www.w3.org/2000/svg" p-id="7110" width="20" height="20">
<path d="M806.3 150.2c-5.4-5.4-14.3-8.9-25-7.1-19.6 0-35.7 16.1-35.7 35.7 0 8.9 3.6 17.8 10.7 25 89.2 73.1 146.3 183.7 146.3 306.8 0 217.6-174.8 392.4-392.4 392.4S117.8 728.1 117.8 510.5c0-123.1 55.3-231.9 142.7-303.2 8.9-7.1 16.1-17.8 16.1-30.3 0-19.6-16.1-35.7-35.7-35.7-8.9 0-16.1 3.6-23.2 8.9-103.5 85.6-169.5 214-169.5 358.5 0 256.9 206.9 463.8 463.8 463.8s463.8-206.9 463.8-463.8c0-144.4-66-272.9-169.5-358.5z"
p-id="7111"></path>
<path d="M512 622.9c19.6 0 35.7-16.1 35.7-35.7V87.8c0-19.6-16.1-35.7-35.7-35.7s-35.7 16.1-35.7 35.7v499.4c0 19.6 16.1 35.7 35.7 35.7z"
p-id="7112"></path>
</svg>
</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 = '人工费用标准新增编辑页.html?action=add';
},
handleEdit(item) {
window.location.href = `人工费用标准新增编辑页.html?action=edit&id=${item.id}`;
},
handleToggleStatus(item) {
item.status = item.status === '启用' ? '停用' : '启用';
}
}
}).mount('#app');
</script>
</div>
</div>
</body>
<script src="./js.js"></script>
<script>
let importstr = document.getElementById('import')
let uploadstr = document.getElementById('batch-upload-modal')
let close = document.getElementById('close-batch-upload-modal')
let cancel = document.getElementById('cancel-batch-upload-btn')
uploadstr.style.display = 'none'
importstr.addEventListener('click', function () {
uploadstr.style.display = 'flex'
})
uploadstr.addEventListener('click', function (e) {
// 阻止事件冒泡,只有点击模态框背景时才关闭
if (e.target === uploadstr) {
uploadstr.style.display = 'none'
}
})
close.addEventListener('click', function () {
uploadstr.style.display = 'none'
})
cancel.addEventListener('click', function () {
uploadstr.style.display = 'none'
})
</script>
</html>
\ No newline at end of file
<!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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #1B64F3;
--primary-hover: #1554D3;
--primary-light: #e6f7ff;
--bg-color: #F5F6FA;
--card-bg: #FFFFFF;
--sidebar-bg: #0F172A;
--text-primary: #1A1F36;
--text-secondary: #64748B;
--text-tertiary: #94A3B8;
--text-light: #CBD5E1;
--border-color: #E2E8F0;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--success-color: #22C55E;
--warning-color: #EAB308;
--error-color: #EF4444;
--sidebar-width: 260px;
--header-height: 64px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 左侧导航栏 */
.sidebar {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 1000;
}
.sidebar-logo {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
}
.logo-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.sidebar-menu {
padding: 16px 0;
}
.nav-group-title {
padding: 12px 20px 8px;
font-size: 11px;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.menu-item, .nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-light);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
position: relative;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.menu-item:hover, .nav-item:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.menu-item.active, .nav-item.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary-color);
}
.nav-item.has-submenu {
justify-content: space-between;
}
.nav-arrow {
width: 14px;
height: 14px;
transition: transform 0.2s;
margin-left: auto;
}
.nav-item.expanded .nav-arrow {
transform: rotate(90deg);
}
.nav-submenu {
display: none;
background: rgba(0, 0, 0, 0.1);
}
.nav-item.expanded + .nav-submenu {
display: block;
}
.nav-subitem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px 10px 48px;
color: var(--text-light);
text-decoration: none;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
position: relative;
}
.nav-subitem:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.nav-subitem.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-icon {
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* 顶部工具栏 */
.header, .top-header {
position: fixed;
top: 0;
left: var(--sidebar-width);
right: 0;
height: var(--header-height);
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
z-index: 999;
box-shadow: var(--shadow-sm);
}
.header-left {
display: flex;
align-items: center;
gap: 24px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.breadcrumb-separator {
color: var(--text-tertiary);
}
.breadcrumb-current {
color: var(--text-primary);
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.header-icon-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 8px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.notification-badge {
position: absolute;
top: 6px;
right: 6px;
width: 8px;
height: 8px;
background: var(--error-color);
border-radius: 50%;
border: 2px solid var(--card-bg);
}
.lang-switcher {
display: flex;
gap: 6px;
align-items: center;
}
.lang-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.lang-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.lang-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
cursor: pointer;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
.user-info {
display: flex;
align-items: center;
gap: 16px;
}
.notification {
position: relative;
cursor: pointer;
color: var(--text-secondary);
}
/* 主内容区域 - 已调整左右内边距 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
min-height: 100vh;
margin-left: 260px;
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
/* 调整左右内边距,从 8px 增加到 32px,以利用更多空间 */
/* padding: 4px 32px;
min-height: calc(100vh - var(--header-height));
.main-content { } */
}
.content-area {
padding: 0;
}
/* 页面标题区域 */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.page-title-main {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.page-title-sub {
font-size: 14px;
color: var(--text-secondary);
}
/* 表格数据优化 */
.table-cell-number {
font-weight: 600;
color: var(--text-primary);
}
.table-cell-date {
color: var(--text-secondary);
font-size: 13px;
}
/* 表格内容样式增强 */
td strong {
color: var(--text-primary);
font-weight: 600;
}
td code {
font-size: 12px;
font-weight: 500;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 48px;
color: var(--text-tertiary);
margin-bottom: 16px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
}
/* 响应式表格优化 */
@media (max-width: 1200px) {
.table-container {
overflow-x: auto;
}
table {
min-width: 1000px;
}
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
th, td {
padding: 12px 16px;
}
.action-buttons {
flex-direction: column;
gap: 6px;
}
.action-btn {
width: 100%;
justify-content: center;
}
}
/* 卡片样式 */
.card {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 24px;
border: 1px solid var(--border-color);
}
.card-header {
padding: 18px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-color);
}
.card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.2px;
}
.card-body {
padding: 20px 16px;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(27, 100, 243, 0.3);
}
.btn-secondary {
background: #f0f0f0;
color: var(--text-light);
}
.btn-secondary:hover {
background: #e0e0e0;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-success:hover {
background: #389e0d;
}
.btn-danger {
background: var(--error-color);
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
/* 标签页样式 */
.tabs {
display: flex;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 10px 0;
color: var(--text-light);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-size: 14px;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
/* 表格样式 */
.table-container {
overflow-x: auto;
border-radius: 8px;
width: 100%;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
}
th, td {
padding: 16px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
tbody tr {
transition: all 0.2s;
}
tbody tr:hover {
background: rgba(27, 100, 243, 0.04);
}
tbody tr:last-child td {
border-bottom: none;
}
td {
color: var(--text-primary);
font-size: 14px;
vertical-align: middle;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status.active {
background: #f6ffed;
color: var(--success-color);
}
.status.inactive {
background: #fff2e8;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.action-btn {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.action-btn i {
font-size: 12px;
}
.action-btn.edit-btn {
background: rgba(27, 100, 243, 0.1);
color: var(--primary-color);
}
.action-btn.edit-btn:hover {
background: rgba(27, 100, 243, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.delete-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--error-color);
}
.action-btn.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}
.action-btn.view-btn {
background: rgba(27, 100, 243, 0.08);
color: var(--primary-color);
}
.action-btn.view-btn:hover {
background: rgba(27, 100, 243, 0.15);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.copy-btn {
background: rgba(34, 197, 94, 0.1);
color: var(--success-color);
}
.action-btn.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.add-modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 800px;
max-height: 85vh;
overflow-y: auto;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
}
.close-modal {
background: none;
border: none;
font-size: 20px;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.3s;
}
.close-modal:hover {
background: var(--secondary-color);
color: var(--text-color);
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
/* 新增表单模态框样式 */
.add-modal-form .form-group {
margin-bottom: 20px;
}
.add-modal-form .form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.add-modal-form .form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.add-modal-form label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--text-secondary);
font-size: 14px;
}
.add-modal-form input,
.add-modal-form select,
.add-modal-form textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.add-modal-form input:focus,
.add-modal-form select:focus,
.add-modal-form textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.add-modal-form .section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.upload-box {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: var(--bg-color);
}
.upload-box:hover {
border-color: var(--primary-color);
background-color: rgba(27, 100, 243, 0.05);
}
.upload-icon {
font-size: 24px;
color: var(--primary-color);
margin-bottom: 10px;
}
.upload-info {
color: var(--text-secondary);
font-size: 13px;
}
.file-name {
margin-top: 10px;
font-weight: 500;
color: var(--primary-color);
font-size: 13px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.param-table th, .param-table td {
border: 1px solid var(--border-color);
padding: 8px;
text-align: left;
}
.param-table th {
background-color: var(--bg-color);
font-weight: 600;
}
.add-param-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
font-size: 14px;
}
.add-param-btn:hover {
background: var(--primary-hover);
}
/* 二维码预览样式 */
.qrcode-preview-large {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: var(--primary-light);
border-radius: 8px;
margin-bottom: 20px;
}
.qrcode-image {
width: 200px;
height: 200px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.qrcode-image img {
max-width: 100%;
max-height: 100%;
}
/* 详情信息样式 */
.detail-info {
margin-top: 20px;
}
.detail-item {
display: flex;
margin-bottom: 12px;
font-size: 14px;
}
.detail-label {
width: 100px;
color: var(--text-light);
flex-shrink: 0;
}
.detail-value {
color: var(--text-color);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show-mobile {
transform: translateX(0);
}
.main-content {
margin-left: 0;
margin-top: 0;
padding: 16px; /* 在移动端保持较小的内边距 */
}
.header, .top-header {
left: 0;
}
.tabs {
flex-wrap: wrap;
}
}
/* 批量上传相关样式 */
.batch-upload-modal .modal-content {
max-width: 800px;
}
.template-selector {
margin-bottom: 20px;
}
.template-selector label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.template-selector select {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: white;
}
.template-download-btn {
margin-top: 10px;
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.template-download-btn:hover {
background: var(--primary-hover);
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s;
background: var(--bg-color);
}
.upload-area:hover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.upload-area.dragover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.1);
}
.upload-icon-large {
font-size: 48px;
color: var(--primary-color);
margin-bottom: 12px;
}
.upload-text {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.upload-hint {
font-size: 12px;
color: var(--text-tertiary);
}
.progress-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.progress-stats {
display: flex;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
}
.stat-label {
color: var(--text-secondary);
}
.stat-value {
font-weight: 600;
color: var(--text-primary);
}
.stat-value.success {
color: var(--success-color);
}
.stat-value.error {
color: var(--error-color);
}
.progress-bar-container {
width: 100%;
height: 8px;
background: var(--bg-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
transition: width 0.3s ease;
border-radius: 4px;
}
.error-list {
margin-top: 16px;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px;
background: #fff5f5;
}
.error-item {
padding: 8px;
margin-bottom: 6px;
background: white;
border-left: 3px solid var(--error-color);
border-radius: 4px;
font-size: 13px;
}
.error-item:last-child {
margin-bottom: 0;
}
.error-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.error-reason {
color: var(--error-color);
font-weight: 500;
}
.download-error-btn {
margin-top: 12px;
padding: 8px 16px;
background: var(--error-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.download-error-btn:hover {
background: #cf1322;
}
.validation-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-size: 13px;
table-layout: fixed;
}
.validation-table th,
.validation-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: normal;
word-break: normal;
overflow: visible;
vertical-align: middle;
}
.validation-table th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.validation-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.validation-table td:nth-child(5) {
white-space: normal;
word-break: break-word;
}
.validation-table th:nth-child(1),
.validation-table td:nth-child(1) {
width: 60px;
min-width: 60px;
text-align: center;
}
.validation-table th:nth-child(2),
.validation-table td:nth-child(2) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(3),
.validation-table td:nth-child(3) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(4),
.validation-table td:nth-child(4) {
width: 100px;
min-width: 100px;
text-align: center;
}
.validation-table th:nth-child(5),
.validation-table td:nth-child(5) {
width: auto;
min-width: 200px;
}
.validation-table tr.error-row {
background: #fff5f5;
}
.validation-table tr.error-row td {
color: var(--error-color);
}
#validation-table-container {
overflow-x: auto;
max-width: 100%;
width: 100%;
}
#validation-table-container table {
min-width: 600px;
}
.file-name-display {
margin-top: 12px;
padding: 10px;
background: var(--bg-color);
border-radius: 6px;
font-size: 13px;
color: var(--text-secondary);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #01337a;
color: #fff;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
td {
padding: 10px;
border: 1px solid #ddd;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 在主页面CSS中添加 */
.sidebar-iframe {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
border: none;
z-index: 999;
}
/* 为main-content和header添加左边距以避开iframe */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
}
.top-header {
left: var(--sidebar-width);
}
</style>
</head>
<body>
<div class="app-container">
<div id="sidebar-container"></div>
<header class="top-header">
<div class="header-left">
<div class="breadcrumb">
<span>首页</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">信息原页面</span>
</div>
</div>
<div class="header-right">
<button class="header-icon-btn" title="通知">
<svg class="nav-icon" viewBox="0 0 20 20">
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
</svg>
<span class="notification-badge"></span>
</button>
<div class="lang-switcher">
<button class="lang-btn active">中文</button>
<button class="lang-btn">EN</button>
</div>
<div class="user-avatar"></div>
</div>
</header>
<div class="main-content" style="position: relative;">
</div>
</div>
</body>
<script src="./js.js"></script>
</html>
\ No newline at end of file
<!DOCTYPE html> <!DOCTYPE html>
...@@ -825,3 +825,4 @@ ...@@ -825,3 +825,4 @@
<!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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #1B64F3;
--primary-hover: #1554D3;
--primary-light: #e6f7ff;
--bg-color: #F5F6FA;
--card-bg: #FFFFFF;
--sidebar-bg: #0F172A;
--text-primary: #1A1F36;
--text-secondary: #64748B;
--text-tertiary: #94A3B8;
--text-light: #CBD5E1;
--border-color: #E2E8F0;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--success-color: #22C55E;
--warning-color: #EAB308;
--error-color: #EF4444;
--sidebar-width: 260px;
--header-height: 64px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 左侧导航栏 */
.sidebar {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 1000;
}
.sidebar-logo {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
}
.logo-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.sidebar-menu {
padding: 16px 0;
}
.nav-group-title {
padding: 12px 20px 8px;
font-size: 11px;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.menu-item, .nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-light);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
position: relative;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.menu-item:hover, .nav-item:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.menu-item.active, .nav-item.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary-color);
}
.nav-item.has-submenu {
justify-content: space-between;
}
.nav-arrow {
width: 14px;
height: 14px;
transition: transform 0.2s;
margin-left: auto;
}
.nav-item.expanded .nav-arrow {
transform: rotate(90deg);
}
.nav-submenu {
display: none;
background: rgba(0, 0, 0, 0.1);
}
.nav-item.expanded + .nav-submenu {
display: block;
}
.nav-subitem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px 10px 48px;
color: var(--text-light);
text-decoration: none;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
position: relative;
}
.nav-subitem:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.nav-subitem.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-icon {
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* 顶部工具栏 */
.header, .top-header {
position: fixed;
top: 0;
left: var(--sidebar-width);
right: 0;
height: var(--header-height);
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
z-index: 999;
box-shadow: var(--shadow-sm);
}
.header-left {
display: flex;
align-items: center;
gap: 24px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.breadcrumb-separator {
color: var(--text-tertiary);
}
.breadcrumb-current {
color: var(--text-primary);
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.header-icon-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 8px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.notification-badge {
position: absolute;
top: 6px;
right: 6px;
width: 8px;
height: 8px;
background: var(--error-color);
border-radius: 50%;
border: 2px solid var(--card-bg);
}
.lang-switcher {
display: flex;
gap: 6px;
align-items: center;
}
.lang-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.lang-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.lang-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
cursor: pointer;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
.user-info {
display: flex;
align-items: center;
gap: 16px;
}
.notification {
position: relative;
cursor: pointer;
color: var(--text-secondary);
}
/* 主内容区域 - 已调整左右内边距 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
min-height: 100vh;
margin-left: 260px;
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
/* 调整左右内边距,从 8px 增加到 32px,以利用更多空间 */
/* padding: 4px 32px;
min-height: calc(100vh - var(--header-height));
.main-content { } */
}
.content-area {
padding: 0;
}
/* 页面标题区域 */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.page-title-main {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.page-title-sub {
font-size: 14px;
color: var(--text-secondary);
}
/* 表格数据优化 */
.table-cell-number {
font-weight: 600;
color: var(--text-primary);
}
.table-cell-date {
color: var(--text-secondary);
font-size: 13px;
}
/* 表格内容样式增强 */
td strong {
color: var(--text-primary);
font-weight: 600;
}
td code {
font-size: 12px;
font-weight: 500;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 48px;
color: var(--text-tertiary);
margin-bottom: 16px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
}
/* 响应式表格优化 */
@media (max-width: 1200px) {
.table-container {
overflow-x: auto;
}
table {
min-width: 1000px;
}
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
th, td {
padding: 12px 16px;
}
.action-buttons {
flex-direction: column;
gap: 6px;
}
.action-btn {
width: 100%;
justify-content: center;
}
}
/* 卡片样式 */
.card {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 24px;
border: 1px solid var(--border-color);
}
.card-header {
padding: 18px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-color);
}
.card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.2px;
}
.card-body {
padding: 20px 16px;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(27, 100, 243, 0.3);
}
.btn-secondary {
background: #f0f0f0;
color: var(--text-light);
}
.btn-secondary:hover {
background: #e0e0e0;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-success:hover {
background: #389e0d;
}
.btn-danger {
background: var(--error-color);
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
/* 标签页样式 */
.tabs {
display: flex;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 10px 0;
color: var(--text-light);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-size: 14px;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
/* 表格样式 */
.table-container {
overflow-x: auto;
border-radius: 8px;
width: 100%;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
}
th, td {
padding: 16px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
tbody tr {
transition: all 0.2s;
}
tbody tr:hover {
background: rgba(27, 100, 243, 0.04);
}
tbody tr:last-child td {
border-bottom: none;
}
td {
color: var(--text-primary);
font-size: 14px;
vertical-align: middle;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status.active {
background: #f6ffed;
color: var(--success-color);
}
.status.inactive {
background: #fff2e8;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.action-btn {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.action-btn i {
font-size: 12px;
}
.action-btn.edit-btn {
background: rgba(27, 100, 243, 0.1);
color: var(--primary-color);
}
.action-btn.edit-btn:hover {
background: rgba(27, 100, 243, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.delete-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--error-color);
}
.action-btn.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}
.action-btn.view-btn {
background: rgba(27, 100, 243, 0.08);
color: var(--primary-color);
}
.action-btn.view-btn:hover {
background: rgba(27, 100, 243, 0.15);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.copy-btn {
background: rgba(34, 197, 94, 0.1);
color: var(--success-color);
}
.action-btn.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.add-modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 800px;
max-height: 85vh;
overflow-y: auto;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
}
.close-modal {
background: none;
border: none;
font-size: 20px;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.3s;
}
.close-modal:hover {
background: var(--secondary-color);
color: var(--text-color);
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
/* 新增表单模态框样式 */
.add-modal-form .form-group {
margin-bottom: 20px;
}
.add-modal-form .form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.add-modal-form .form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.add-modal-form label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--text-secondary);
font-size: 14px;
}
.add-modal-form input,
.add-modal-form select,
.add-modal-form textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.add-modal-form input:focus,
.add-modal-form select:focus,
.add-modal-form textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.add-modal-form .section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.upload-box {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: var(--bg-color);
}
.upload-box:hover {
border-color: var(--primary-color);
background-color: rgba(27, 100, 243, 0.05);
}
.upload-icon {
font-size: 24px;
color: var(--primary-color);
margin-bottom: 10px;
}
.upload-info {
color: var(--text-secondary);
font-size: 13px;
}
.file-name {
margin-top: 10px;
font-weight: 500;
color: var(--primary-color);
font-size: 13px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.param-table th, .param-table td {
border: 1px solid var(--border-color);
padding: 8px;
text-align: left;
}
.param-table th {
background-color: var(--bg-color);
font-weight: 600;
}
.add-param-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
font-size: 14px;
}
.add-param-btn:hover {
background: var(--primary-hover);
}
/* 二维码预览样式 */
.qrcode-preview-large {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: var(--primary-light);
border-radius: 8px;
margin-bottom: 20px;
}
.qrcode-image {
width: 200px;
height: 200px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.qrcode-image img {
max-width: 100%;
max-height: 100%;
}
/* 详情信息样式 */
.detail-info {
margin-top: 20px;
}
.detail-item {
display: flex;
margin-bottom: 12px;
font-size: 14px;
}
.detail-label {
width: 100px;
color: var(--text-light);
flex-shrink: 0;
}
.detail-value {
color: var(--text-color);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show-mobile {
transform: translateX(0);
}
.main-content {
margin-left: 0;
margin-top: 0;
padding: 16px; /* 在移动端保持较小的内边距 */
}
.header, .top-header {
left: 0;
}
.tabs {
flex-wrap: wrap;
}
}
/* 批量上传相关样式 */
.batch-upload-modal .modal-content {
max-width: 800px;
}
.template-selector {
margin-bottom: 20px;
}
.template-selector label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.template-selector select {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: white;
}
.template-download-btn {
margin-top: 10px;
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.template-download-btn:hover {
background: var(--primary-hover);
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s;
background: var(--bg-color);
}
.upload-area:hover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.upload-area.dragover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.1);
}
.upload-icon-large {
font-size: 48px;
color: var(--primary-color);
margin-bottom: 12px;
}
.upload-text {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.upload-hint {
font-size: 12px;
color: var(--text-tertiary);
}
.progress-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.progress-stats {
display: flex;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
}
.stat-label {
color: var(--text-secondary);
}
.stat-value {
font-weight: 600;
color: var(--text-primary);
}
.stat-value.success {
color: var(--success-color);
}
.stat-value.error {
color: var(--error-color);
}
.progress-bar-container {
width: 100%;
height: 8px;
background: var(--bg-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
transition: width 0.3s ease;
border-radius: 4px;
}
.error-list {
margin-top: 16px;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px;
background: #fff5f5;
}
.error-item {
padding: 8px;
margin-bottom: 6px;
background: white;
border-left: 3px solid var(--error-color);
border-radius: 4px;
font-size: 13px;
}
.error-item:last-child {
margin-bottom: 0;
}
.error-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.error-reason {
color: var(--error-color);
font-weight: 500;
}
.download-error-btn {
margin-top: 12px;
padding: 8px 16px;
background: var(--error-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.download-error-btn:hover {
background: #cf1322;
}
.validation-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-size: 13px;
table-layout: fixed;
}
.validation-table th,
.validation-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: normal;
word-break: normal;
overflow: visible;
vertical-align: middle;
}
.validation-table th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.validation-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.validation-table td:nth-child(5) {
white-space: normal;
word-break: break-word;
}
.validation-table th:nth-child(1),
.validation-table td:nth-child(1) {
width: 60px;
min-width: 60px;
text-align: center;
}
.validation-table th:nth-child(2),
.validation-table td:nth-child(2) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(3),
.validation-table td:nth-child(3) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(4),
.validation-table td:nth-child(4) {
width: 100px;
min-width: 100px;
text-align: center;
}
.validation-table th:nth-child(5),
.validation-table td:nth-child(5) {
width: auto;
min-width: 200px;
}
.validation-table tr.error-row {
background: #fff5f5;
}
.validation-table tr.error-row td {
color: var(--error-color);
}
#validation-table-container {
overflow-x: auto;
max-width: 100%;
width: 100%;
}
#validation-table-container table {
min-width: 600px;
}
.file-name-display {
margin-top: 12px;
padding: 10px;
background: var(--bg-color);
border-radius: 6px;
font-size: 13px;
color: var(--text-secondary);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #01337a;
color: #fff;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
td {
padding: 10px;
border: 1px solid #ddd;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 在主页面CSS中添加 */
.sidebar-iframe {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
border: none;
z-index: 999;
}
/* 为main-content和header添加左边距以避开iframe */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
}
.top-header {
left: var(--sidebar-width);
}
</style>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
a{
text-decoration: none;
}
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;
}
.header-actions {
display: flex;
gap: 10px;
}
.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;
display: flex;
justify-content: space-between;
align-items: center;
}
.filter-bar {
display: flex;
gap: 15px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.filter-item {
flex: 1;
min-width: 150px;
}
.filter-item label {
display: block;
margin-bottom: 5px;
color: #606266;
font-size: 14px;
}
.filter-item input,
.filter-item select {
width: 100%;
padding: 8px 12px;
border: 1px solid #dcdfe6;
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.filter-item input:focus,
.filter-item select:focus {
outline: none;
border-color: #409eff;
}
.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-danger {
background: #f56c6c;
color: white;
}
.btn-danger:hover {
background: #f78989;
}
.btn-default {
background: #f5f7fa;
color: #606266;
border: 1px solid #dcdfe6;
}
.btn-default:hover {
background: #ecf5ff;
color: #409eff;
border-color: #409eff;
}
.btn-sm {
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;
}
.data-table tr:last-child td {
border-bottom: none;
}
.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;
}
.status-badge.expired {
background: #fef0f0;
color: #f56c6c;
border: 1px solid #f56c6c;
}
.status-badge.pending {
background: #fdf6ec;
color: #e6a23c;
border: 1px solid #e6a23c;
}
.quote-no {
color: #409eff;
font-weight: 600;
cursor: pointer;
text-decoration: none;
}
.quote-no:hover {
text-decoration: underline;
}
.empty-state {
text-align: center;
padding: 60px 20px;
color: #909399;
}
.empty-state-icon {
font-size: 64px;
margin-bottom: 15px;
}
.action-buttons {
display: flex;
gap: 8px;
}
.stats-bar {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background: linear-gradient(135deg, #409eff 0%, #66b1ff 100%);
color: white;
padding: 20px;
border-radius: 8px;
text-align: center;
}
.stat-card.success {
background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
}
.stat-card.warning {
background: linear-gradient(135deg, #e6a23c 0%, #ebb563 100%);
}
.stat-value {
font-size: 32px;
font-weight: bold;
margin: 10px 0;
}
.stat-label {
font-size: 14px;
opacity: 0.9;
}
</style>
</head>
<body>
<div class="app-container">
<div id="sidebar-container"></div>
<header class="top-header">
<div class="header-left">
<div class="breadcrumb">
<span>首页</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">服务单列表</span>
</div>
</div>
<div class="header-right">
<button class="header-icon-btn" title="通知">
<svg class="nav-icon" viewBox="0 0 20 20">
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
</svg>
<span class="notification-badge"></span>
</button>
<div class="lang-switcher">
<button class="lang-btn active">中文</button>
<button class="lang-btn">EN</button>
</div>
<div class="user-avatar"></div>
</div>
</header>
<div class="main-content" style="position: relative;">
<div id="app">
<div class="container card" style="margin: 0;">
<!-- 统计卡片 -->
<div class="stats-bar">
<div class="stat-card">
<div class="stat-label">总报价单数</div>
<div class="stat-value">{{ stats.total }}</div>
</div>
<div class="stat-card success">
<div class="stat-label">有效报价单</div>
<div class="stat-value">{{ stats.active }}</div>
</div>
<div class="stat-card warning">
<div class="stat-label">已过期</div>
<div class="stat-value">{{ stats.expired }}</div>
</div>
<div class="stat-card">
<div class="stat-label">总金额</div>
<div class="stat-value">¥{{ stats.totalAmount.toLocaleString() }}</div>
</div>
</div>
<div class="">
<div class="card-title">
<span>报价单列表 (共 {{ filteredQuotes.length }} 条)</span>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<label>报价单编号</label>
<input type="text" v-model="filters.quoteNo" @input="applyFilters" placeholder="输入报价单编号">
</div>
<div class="filter-item">
<label>客户名称</label>
<input type="text" v-model="filters.customerName" @input="applyFilters" placeholder="输入客户名称">
</div>
<div class="filter-item">
<label>状态</label>
<select v-model="filters.status" @change="applyFilters">
<option value="">全部状态</option>
<option value="有效">有效</option>
<option value="已过期">已过期</option>
<option value="待确认">待确认</option>
</select>
</div>
<div class="filter-item">
<label>报价日期</label>
<input type="date" v-model="filters.quoteDate" @change="applyFilters">
</div>
</div>
<!-- 数据表格 -->
<table class="data-table" v-if="filteredQuotes.length > 0">
<thead>
<tr>
<th>报价单编号</th>
<th>客户名称</th>
<th>服务内容</th>
<th>报价日期</th>
<th>有效期至</th>
<th>总金额</th>
<th>凭证</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr v-for="quote in filteredQuotes" :key="quote.quoteNo">
<td>
<a :href="'标准报价单详情页.html?quoteNo=' + quote.quoteNo" class="quote-no">
{{ quote.quoteNo }}
</a>
</td>
<td>{{ quote.customerName }}</td>
<td style="max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
{{ quote.serviceDescription }}
</td>
<td>{{ formatDate(quote.quoteDate) }}</td>
<td>{{ formatDate(quote.validUntil) }}</td>
<td style="font-weight: 600; color: #409eff;">¥{{ quote.totalAmount.toFixed(2) }}</td>
<td>未上传凭证</td>
<td>
<span class="status-badge" :class="getStatusClass(quote.status)">
{{ quote.status }}
</span>
</td>
<td>
<div class="action-buttons">
<a :href="'标准报价单详情页.html?quoteNo=' + quote.quoteNo" class="btn btn-primary btn-sm">查看</a>
<button @click="exportQuote(quote)" class="btn btn-success btn-sm">导出</button>
<!-- <button @click="deleteQuote(quote.quoteNo)" class="btn btn-danger btn-sm">删除</button> -->
<!-- 上传列表 -->
<button class="btn btn-primary btn-sm">上传凭证</button>
</div>
</td>
</tr>
</tbody>
</table>
<div class="empty-state" v-else>
<div class="empty-state-icon">📋</div>
<div>暂无报价单</div>
<div style="margin-top: 15px;">
<a href="8-标准报价单生成页.html" class="btn btn-primary">生成新报价单</a>
</div>
</div>
</div>
</div>
</div>
<script>
const { createApp } = Vue;
createApp({
data() {
return {
quotes: [],
filters: {
quoteNo: '',
customerName: '',
status: '',
quoteDate: ''
}
}
},
computed: {
stats() {
const now = new Date().getTime();
const active = this.quotes.filter(q => {
const validUntil = new Date(q.validUntil).getTime();
return q.status === '有效' && validUntil >= now;
}).length;
const expired = this.quotes.filter(q => {
const validUntil = new Date(q.validUntil).getTime();
return validUntil < now || q.status === '已过期';
}).length;
const totalAmount = this.quotes.reduce((sum, q) => sum + (q.totalAmount || 0), 0);
return {
total: this.quotes.length,
active: active,
expired: expired,
totalAmount: totalAmount
};
},
filteredQuotes() {
let result = [...this.quotes];
if (this.filters.quoteNo) {
result = result.filter(q =>
q.quoteNo.toLowerCase().includes(this.filters.quoteNo.toLowerCase())
);
}
if (this.filters.customerName) {
result = result.filter(q =>
q.customerName.toLowerCase().includes(this.filters.customerName.toLowerCase())
);
}
if (this.filters.status) {
result = result.filter(q => q.status === this.filters.status);
}
if (this.filters.quoteDate) {
const filterDate = new Date(this.filters.quoteDate).toDateString();
result = result.filter(q => {
const quoteDate = new Date(q.quoteDate).toDateString();
return quoteDate === filterDate;
});
}
// 按报价日期倒序排列
return result.sort((a, b) => {
return new Date(b.quoteDate) - new Date(a.quoteDate);
});
}
},
mounted() {
this.loadQuotes();
// 监听报价单生成事件(从生成页跳转过来时)
this.checkNewQuote();
},
methods: {
loadQuotes() {
const saved = localStorage.getItem('serviceQuotes');
if (saved) {
this.quotes = JSON.parse(saved);
// 更新状态(检查是否过期)
this.updateQuoteStatus();
} else {
// 初始化示例数据
this.quotes = [
{
quoteNo: 'QT' + (Date.now() - 86400000),
customerName: '示例客户有限公司',
serviceDescription: '服务器故障维修服务,包括硬件检测、故障排查、零件更换等',
quoteDate: new Date(Date.now() - 2 * 86400000).toISOString().split('T')[0],
validUntil: new Date(Date.now() + 28 * 86400000).toISOString().split('T')[0],
status: '有效',
totalAmount: 2050.00,
laborDetails: [],
partDetails: []
}
];
this.saveQuotes();
}
},
saveQuotes() {
localStorage.setItem('serviceQuotes', JSON.stringify(this.quotes));
},
updateQuoteStatus() {
const now = new Date().getTime();
this.quotes.forEach(quote => {
const validUntil = new Date(quote.validUntil).getTime();
if (validUntil < now && quote.status === '有效') {
quote.status = '已过期';
}
});
this.saveQuotes();
},
checkNewQuote() {
// 检查URL参数,看是否有新生成的报价单
const urlParams = new URLSearchParams(window.location.search);
const newQuoteNo = urlParams.get('newQuote');
if (newQuoteNo) {
// 从生成页传递的数据中获取报价单信息
const quoteData = sessionStorage.getItem('newQuoteData');
if (quoteData) {
const newQuote = JSON.parse(quoteData);
// 检查是否已存在
const exists = this.quotes.find(q => q.quoteNo === newQuote.quoteNo);
if (!exists) {
this.quotes.unshift(newQuote);
this.saveQuotes();
}
sessionStorage.removeItem('newQuoteData');
}
}
},
applyFilters() {
// 筛选逻辑在computed中处理
},
getStatusClass(status) {
if (status === '有效') return 'active';
if (status === '已过期') return 'expired';
if (status === '待确认') return 'pending';
return '';
},
formatDate(dateStr) {
if (!dateStr) return '-';
const date = new Date(dateStr);
return date.toLocaleDateString('zh-CN');
},
exportQuote(quote) {
alert('导出功能(占位)\n报价单编号:' + quote.quoteNo + '\n将导出为PDF或Excel格式');
},
deleteQuote(quoteNo) {
if (confirm('确定要删除报价单 ' + quoteNo + ' 吗?')) {
this.quotes = this.quotes.filter(q => q.quoteNo !== quoteNo);
this.saveQuotes();
alert('删除成功');
}
}
}
}).mount('#app');
</script>
</div>
</div>
</body>
<script src="./js.js"></script>
</html>
\ No newline at end of file
<!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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<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);
}
.btn-small {
padding: 6px 12px;
font-size: 12px;
}
.btn-danger {
background: #f56c6c;
color: white;
}
.btn-danger:hover {
background: #f78989;
}
</style>
<style>
:root {
--primary-color: #1B64F3;
--primary-hover: #1554D3;
--primary-light: #e6f7ff;
--bg-color: #F5F6FA;
--card-bg: #FFFFFF;
--sidebar-bg: #0F172A;
--text-primary: #1A1F36;
--text-secondary: #64748B;
--text-tertiary: #94A3B8;
--text-light: #CBD5E1;
--border-color: #E2E8F0;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--success-color: #22C55E;
--warning-color: #EAB308;
--error-color: #EF4444;
--sidebar-width: 260px;
--header-height: 64px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 左侧导航栏 */
.sidebar {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 1000;
}
.sidebar-logo {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
}
.logo-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.sidebar-menu {
padding: 16px 0;
}
.nav-group-title {
padding: 12px 20px 8px;
font-size: 11px;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.menu-item, .nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-light);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
position: relative;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.menu-item:hover, .nav-item:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.menu-item.active, .nav-item.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary-color);
}
.nav-item.has-submenu {
justify-content: space-between;
}
.nav-arrow {
width: 14px;
height: 14px;
transition: transform 0.2s;
margin-left: auto;
}
.nav-item.expanded .nav-arrow {
transform: rotate(90deg);
}
.nav-submenu {
display: none;
background: rgba(0, 0, 0, 0.1);
}
.nav-item.expanded + .nav-submenu {
display: block;
}
.nav-subitem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px 10px 48px;
color: var(--text-light);
text-decoration: none;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
position: relative;
}
.nav-subitem:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.nav-subitem.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-icon {
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* 顶部工具栏 */
.header, .top-header {
position: fixed;
top: 0;
left: var(--sidebar-width);
right: 0;
height: var(--header-height);
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
z-index: 999;
box-shadow: var(--shadow-sm);
}
.header-left {
display: flex;
align-items: center;
gap: 24px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.breadcrumb-separator {
color: var(--text-tertiary);
}
.breadcrumb-current {
color: var(--text-primary);
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.header-icon-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 8px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.notification-badge {
position: absolute;
top: 6px;
right: 6px;
width: 8px;
height: 8px;
background: var(--error-color);
border-radius: 50%;
border: 2px solid var(--card-bg);
}
.lang-switcher {
display: flex;
gap: 6px;
align-items: center;
}
.lang-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.lang-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.lang-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
cursor: pointer;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
.user-info {
display: flex;
align-items: center;
gap: 16px;
}
.notification {
position: relative;
cursor: pointer;
color: var(--text-secondary);
}
/* 主内容区域 - 已调整左右内边距 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
min-height: 100vh;
margin-left: 260px;
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
/* 调整左右内边距,从 8px 增加到 32px,以利用更多空间 */
/* padding: 4px 32px;
min-height: calc(100vh - var(--header-height));
.main-content { } */
}
.content-area {
padding: 0;
}
/* 页面标题区域 */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.page-title-main {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.page-title-sub {
font-size: 14px;
color: var(--text-secondary);
}
/* 表格数据优化 */
.table-cell-number {
font-weight: 600;
color: var(--text-primary);
}
.table-cell-date {
color: var(--text-secondary);
font-size: 13px;
}
/* 表格内容样式增强 */
td strong {
color: var(--text-primary);
font-weight: 600;
}
td code {
font-size: 12px;
font-weight: 500;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 48px;
color: var(--text-tertiary);
margin-bottom: 16px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
}
/* 响应式表格优化 */
@media (max-width: 1200px) {
.table-container {
overflow-x: auto;
}
table {
min-width: 1000px;
}
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
th, td {
padding: 12px 16px;
}
.action-buttons {
flex-direction: column;
gap: 6px;
}
.action-btn {
width: 100%;
justify-content: center;
}
}
/* 卡片样式 */
.card {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 24px;
border: 1px solid var(--border-color);
}
.card-header {
padding: 18px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-color);
}
.card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.2px;
}
.card-body {
padding: 20px 16px;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(27, 100, 243, 0.3);
}
.btn-secondary {
background: #f0f0f0;
color: var(--text-light);
}
.btn-secondary:hover {
background: #e0e0e0;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-success:hover {
background: #389e0d;
}
.btn-danger {
background: var(--error-color);
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
/* 标签页样式 */
.tabs {
display: flex;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 10px 0;
color: var(--text-light);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-size: 14px;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
/* 表格样式 */
.table-container {
overflow-x: auto;
border-radius: 8px;
width: 100%;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
}
th, td {
padding: 16px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
tbody tr {
transition: all 0.2s;
}
tbody tr:hover {
background: rgba(27, 100, 243, 0.04);
}
tbody tr:last-child td {
border-bottom: none;
}
td {
color: var(--text-primary);
font-size: 14px;
vertical-align: middle;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status.active {
background: #f6ffed;
color: var(--success-color);
}
.status.inactive {
background: #fff2e8;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.action-btn {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.action-btn i {
font-size: 12px;
}
.action-btn.edit-btn {
background: rgba(27, 100, 243, 0.1);
color: var(--primary-color);
}
.action-btn.edit-btn:hover {
background: rgba(27, 100, 243, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.delete-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--error-color);
}
.action-btn.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}
.action-btn.view-btn {
background: rgba(27, 100, 243, 0.08);
color: var(--primary-color);
}
.action-btn.view-btn:hover {
background: rgba(27, 100, 243, 0.15);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.copy-btn {
background: rgba(34, 197, 94, 0.1);
color: var(--success-color);
}
.action-btn.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.add-modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 800px;
max-height: 85vh;
overflow-y: auto;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
}
.close-modal {
background: none;
border: none;
font-size: 20px;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.3s;
}
.close-modal:hover {
background: var(--secondary-color);
color: var(--text-color);
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
/* 新增表单模态框样式 */
.add-modal-form .form-group {
margin-bottom: 20px;
}
.add-modal-form .form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.add-modal-form .form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.add-modal-form label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--text-secondary);
font-size: 14px;
}
.add-modal-form input,
.add-modal-form select,
.add-modal-form textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.add-modal-form input:focus,
.add-modal-form select:focus,
.add-modal-form textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.add-modal-form .section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.upload-box {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: var(--bg-color);
}
.upload-box:hover {
border-color: var(--primary-color);
background-color: rgba(27, 100, 243, 0.05);
}
.upload-icon {
font-size: 24px;
color: var(--primary-color);
margin-bottom: 10px;
}
.upload-info {
color: var(--text-secondary);
font-size: 13px;
}
.file-name {
margin-top: 10px;
font-weight: 500;
color: var(--primary-color);
font-size: 13px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.param-table th, .param-table td {
border: 1px solid var(--border-color);
padding: 8px;
text-align: left;
}
.param-table th {
background-color: var(--bg-color);
font-weight: 600;
}
.add-param-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
font-size: 14px;
}
.add-param-btn:hover {
background: var(--primary-hover);
}
/* 二维码预览样式 */
.qrcode-preview-large {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: var(--primary-light);
border-radius: 8px;
margin-bottom: 20px;
}
.qrcode-image {
width: 200px;
height: 200px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.qrcode-image img {
max-width: 100%;
max-height: 100%;
}
/* 详情信息样式 */
.detail-info {
margin-top: 20px;
}
.detail-item {
display: flex;
margin-bottom: 12px;
font-size: 14px;
}
.detail-label {
width: 100px;
color: var(--text-light);
flex-shrink: 0;
}
.detail-value {
color: var(--text-color);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show-mobile {
transform: translateX(0);
}
.main-content {
margin-left: 0;
margin-top: 0;
padding: 16px; /* 在移动端保持较小的内边距 */
}
.header, .top-header {
left: 0;
}
.tabs {
flex-wrap: wrap;
}
}
/* 批量上传相关样式 */
.batch-upload-modal .modal-content {
max-width: 800px;
}
.template-selector {
margin-bottom: 20px;
}
.template-selector label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.template-selector select {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: white;
}
.template-download-btn {
margin-top: 10px;
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.template-download-btn:hover {
background: var(--primary-hover);
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s;
background: var(--bg-color);
}
.upload-area:hover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.upload-area.dragover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.1);
}
.upload-icon-large {
font-size: 48px;
color: var(--primary-color);
margin-bottom: 12px;
}
.upload-text {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.upload-hint {
font-size: 12px;
color: var(--text-tertiary);
}
.progress-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.progress-stats {
display: flex;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
}
.stat-label {
color: var(--text-secondary);
}
.stat-value {
font-weight: 600;
color: var(--text-primary);
}
.stat-value.success {
color: var(--success-color);
}
.stat-value.error {
color: var(--error-color);
}
.progress-bar-container {
width: 100%;
height: 8px;
background: var(--bg-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
transition: width 0.3s ease;
border-radius: 4px;
}
.error-list {
margin-top: 16px;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px;
background: #fff5f5;
}
.error-item {
padding: 8px;
margin-bottom: 6px;
background: white;
border-left: 3px solid var(--error-color);
border-radius: 4px;
font-size: 13px;
}
.error-item:last-child {
margin-bottom: 0;
}
.error-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.error-reason {
color: var(--error-color);
font-weight: 500;
}
.download-error-btn {
margin-top: 12px;
padding: 8px 16px;
background: var(--error-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.download-error-btn:hover {
background: #cf1322;
}
.validation-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-size: 13px;
table-layout: fixed;
}
.validation-table th,
.validation-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: normal;
word-break: normal;
overflow: visible;
vertical-align: middle;
}
.validation-table th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.validation-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.validation-table td:nth-child(5) {
white-space: normal;
word-break: break-word;
}
.validation-table th:nth-child(1),
.validation-table td:nth-child(1) {
width: 60px;
min-width: 60px;
text-align: center;
}
.validation-table th:nth-child(2),
.validation-table td:nth-child(2) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(3),
.validation-table td:nth-child(3) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(4),
.validation-table td:nth-child(4) {
width: 100px;
min-width: 100px;
text-align: center;
}
.validation-table th:nth-child(5),
.validation-table td:nth-child(5) {
width: auto;
min-width: 200px;
}
.validation-table tr.error-row {
background: #fff5f5;
}
.validation-table tr.error-row td {
color: var(--error-color);
}
#validation-table-container {
overflow-x: auto;
max-width: 100%;
width: 100%;
}
#validation-table-container table {
min-width: 600px;
}
.file-name-display {
margin-top: 12px;
padding: 10px;
background: var(--bg-color);
border-radius: 6px;
font-size: 13px;
color: var(--text-secondary);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #01337a;
color: #fff;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
td {
padding: 10px;
border: 1px solid #ddd;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 在主页面CSS中添加 */
.sidebar-iframe {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
border: none;
z-index: 999;
}
/* 为main-content和header添加左边距以避开iframe */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
}
.top-header {
left: var(--sidebar-width);
}
</style>
</head>
<body>
<div class="app-container">
<div id="sidebar-container"></div>
<header class="top-header">
<div class="header-left">
<div class="breadcrumb">
<span>首页</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">设备档案</span>
</div>
</div>
<div class="header-right">
<button class="header-icon-btn" title="通知">
<svg class="nav-icon" viewBox="0 0 20 20">
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
</svg>
<span class="notification-badge"></span>
</button>
<div class="lang-switcher">
<button class="lang-btn active">中文</button>
<button class="lang-btn">EN</button>
</div>
<div class="user-avatar"></div>
</div>
</header>
<div class="main-content" style="position: relative;">
<div id="app">
<div class="container">
<div class="card">
<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">
<div class="quote-detail-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>
<select v-model="item.serviceType" class="form-control" @change="updateLaborInfo(index)">
<option value="">请选择服务类型</option>
<option v-for="type in serviceTypes" :key="type" :value="type">{{ type }}</option>
</select>
</td>
<td>
<select v-model="item.techLevel" class="form-control" @change="updateLaborInfo(index)">
<option value="">请选择技术等级</option>
<option v-for="level in techLevels" :key="level" :value="level">{{ level }}</option>
</select>
</td>
<td>
<select v-model="item.billingMethod" class="form-control" @change="updateLaborInfo(index)">
<option value="">请选择计费方式</option>
<option v-for="method in billingMethods" :key="method" :value="method">{{ method }}</option>
</select>
</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 === '按小时' ? '小时' : (item.billingMethod === '按次' ? '次' : '') }}</span>
</td>
<td>¥{{ item.price }}/{{ item.billingMethod === '按小时' ? '小时' : (item.billingMethod === '按次' ? '次' : '') }}</td>
<td>¥{{ (item.price * item.quantity).toFixed(2) }}</td>
<td>
<button type="button" class="btn btn-danger btn-small" @click="removeLabor(index)">删除</button>
</td>
</tr>
<tr>
<td colspan="7">
<button type="button" class="btn btn-primary btn-small" @click="addLabor">+ 添加人工费用</button>
</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="laborDetails.length > 0 || partDetails.length > 0">
<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 }
],
serviceTypes: ['上门', '远程'],
techLevels: ['普通', '高级'],
billingMethods: ['按小时', '按次'],
availableLaborStandards: [
{ serviceType: '上门', techLevel: '普通', billingMethod: '按小时', price: 150 },
{ serviceType: '上门', techLevel: '高级', billingMethod: '按小时', price: 250 },
{ serviceType: '远程', techLevel: '普通', billingMethod: '按次', price: 200 },
{ serviceType: '远程', techLevel: '高级', billingMethod: '按次', price: 300 }
],
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.length === 0) {
// 自动添加推荐的人工费用项
this.laborDetails = this.matchedRule.laborRules.map(rule => ({
serviceType: rule.serviceType,
techLevel: rule.techLevel,
billingMethod: rule.billingMethod,
price: rule.price,
quantity: 1
}));
}
} else {
this.matchedRule = null;
// 不清空人工费用明细,允许用户保留已添加的项
}
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();
},
updateLaborInfo(index) {
const item = this.laborDetails[index];
if (item.serviceType && item.techLevel && item.billingMethod) {
const standard = this.availableLaborStandards.find(s =>
s.serviceType === item.serviceType &&
s.techLevel === item.techLevel &&
s.billingMethod === item.billingMethod
);
if (standard) {
item.price = standard.price;
if (!item.quantity || item.quantity === 0) {
item.quantity = 1;
}
} else {
// 如果没有找到匹配的标准,设置默认价格
item.price = 0;
}
} else {
item.price = 0;
}
this.calculateTotal();
},
addLabor() {
this.laborDetails.push({
serviceType: '',
techLevel: '',
billingMethod: '',
price: 0,
quantity: 0
});
},
removeLabor(index) {
this.laborDetails.splice(index, 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();
// 构建报价单数据
const quoteData = {
quoteNo: quoteNo,
customerName: this.form.customerName,
serviceDescription: this.form.serviceDescription,
quoteDate: new Date().toISOString().split('T')[0],
validUntil: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
status: '有效',
totalAmount: this.totalAmount,
laborDetails: this.laborDetails.filter(item =>
item.serviceType && item.techLevel && item.billingMethod && item.price > 0
).map(item => ({
serviceType: item.serviceType,
techLevel: item.techLevel,
billingMethod: item.billingMethod,
price: item.price,
quantity: item.quantity || 0
})),
partDetails: this.partDetails.filter(item => item.partId).map(item => ({
partName: item.partName,
specification: item.specification,
price: item.price,
quantity: item.quantity || 0
})),
ruleName: this.matchedRule.ruleName,
scenario: this.matchedRule.scenario,
whitebookVersion: 'V1.0',
remark: ''
};
// 保存到localStorage
const saved = localStorage.getItem('serviceQuotes');
const quotes = saved ? JSON.parse(saved) : [];
quotes.unshift(quoteData);
localStorage.setItem('serviceQuotes', JSON.stringify(quotes));
// 跳转到详情页
window.location.href = `标准报价单详情页.html?quoteNo=${quoteNo}`;
},
handleCancel() {
if (confirm('确定要取消吗?未保存的数据将丢失。')) {
window.location.href = '1-服务价格标准首页.html';
}
}
}
}).mount('#app');
</script>
</div>
</div>
</body>
<script src="./js.js"></script>
</html>
\ No newline at end of file
<!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>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #1B64F3;
--primary-hover: #1554D3;
--primary-light: #e6f7ff;
--bg-color: #F5F6FA;
--card-bg: #FFFFFF;
--sidebar-bg: #0F172A;
--text-primary: #1A1F36;
--text-secondary: #64748B;
--text-tertiary: #94A3B8;
--text-light: #CBD5E1;
--border-color: #E2E8F0;
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
--success-color: #22C55E;
--warning-color: #EAB308;
--error-color: #EF4444;
--sidebar-width: 260px;
--header-height: 64px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica Neue', Arial, 'PingFang SC', 'Microsoft YaHei', sans-serif;
background-color: var(--bg-color);
color: var(--text-primary);
line-height: 1.6;
overflow-x: hidden;
}
.app-container {
display: flex;
min-height: 100vh;
}
/* 左侧导航栏 */
.sidebar {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
background: var(--sidebar-bg);
border-right: 1px solid rgba(255, 255, 255, 0.1);
overflow-y: auto;
z-index: 1000;
}
.sidebar-logo {
padding: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
gap: 12px;
}
.logo-icon {
width: 36px;
height: 36px;
background: var(--primary-color);
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: 700;
font-size: 18px;
}
.logo-text {
color: white;
font-size: 16px;
font-weight: 600;
}
.sidebar-menu {
padding: 16px 0;
}
.nav-group-title {
padding: 12px 20px 8px;
font-size: 11px;
font-weight: 600;
color: var(--text-tertiary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.menu-item, .nav-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
color: var(--text-light);
text-decoration: none;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
position: relative;
cursor: pointer;
border: none;
background: none;
width: 100%;
text-align: left;
}
.menu-item:hover, .nav-item:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.menu-item.active, .nav-item.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-item.active::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 3px;
background: var(--primary-color);
}
.nav-item.has-submenu {
justify-content: space-between;
}
.nav-arrow {
width: 14px;
height: 14px;
transition: transform 0.2s;
margin-left: auto;
}
.nav-item.expanded .nav-arrow {
transform: rotate(90deg);
}
.nav-submenu {
display: none;
background: rgba(0, 0, 0, 0.1);
}
.nav-item.expanded + .nav-submenu {
display: block;
}
.nav-subitem {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 20px 10px 48px;
color: var(--text-light);
text-decoration: none;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
position: relative;
}
.nav-subitem:hover {
background: rgba(27, 100, 243, 0.15);
color: white;
}
.nav-subitem.active {
background: rgba(27, 100, 243, 0.2);
color: white;
}
.nav-icon {
width: 18px;
height: 18px;
stroke: currentColor;
stroke-width: 2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* 顶部工具栏 */
.header, .top-header {
position: fixed;
top: 0;
left: var(--sidebar-width);
right: 0;
height: var(--header-height);
background: var(--card-bg);
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
z-index: 999;
box-shadow: var(--shadow-sm);
}
.header-left {
display: flex;
align-items: center;
gap: 24px;
}
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: var(--text-secondary);
}
.breadcrumb-separator {
color: var(--text-tertiary);
}
.breadcrumb-current {
color: var(--text-primary);
font-weight: 500;
}
.header-right {
display: flex;
align-items: center;
gap: 16px;
}
.header-icon-btn {
position: relative;
width: 36px;
height: 36px;
border-radius: 8px;
border: none;
background: transparent;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-secondary);
transition: all 0.2s;
}
.header-icon-btn:hover {
background: var(--bg-color);
color: var(--primary-color);
}
.notification-badge {
position: absolute;
top: 6px;
right: 6px;
width: 8px;
height: 8px;
background: var(--error-color);
border-radius: 50%;
border: 2px solid var(--card-bg);
}
.lang-switcher {
display: flex;
gap: 6px;
align-items: center;
}
.lang-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 0.2s;
}
.lang-btn:hover {
border-color: var(--primary-color);
color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.lang-btn.active {
background: var(--primary-color);
border-color: var(--primary-color);
color: white;
}
.user-avatar {
width: 36px;
height: 36px;
border-radius: 8px;
background: var(--primary-color);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
cursor: pointer;
}
.page-title {
font-size: 20px;
font-weight: 600;
color: var(--text-primary);
}
.user-info {
display: flex;
align-items: center;
gap: 16px;
}
.notification {
position: relative;
cursor: pointer;
color: var(--text-secondary);
}
/* 主内容区域 - 已调整左右内边距 */
.main-content {
flex: 1;
padding: 24px;
overflow-y: auto;
min-height: 100vh;
margin-left: 260px;
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
/* 调整左右内边距,从 8px 增加到 32px,以利用更多空间 */
/* padding: 4px 32px;
min-height: calc(100vh - var(--header-height));
.main-content { } */
}
.content-area {
padding: 0;
}
/* 页面标题区域 */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.page-title-section {
display: flex;
flex-direction: column;
gap: 4px;
}
.page-title-main {
font-size: 24px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.3px;
}
.page-title-sub {
font-size: 14px;
color: var(--text-secondary);
}
/* 表格数据优化 */
.table-cell-number {
font-weight: 600;
color: var(--text-primary);
}
.table-cell-date {
color: var(--text-secondary);
font-size: 13px;
}
/* 表格内容样式增强 */
td strong {
color: var(--text-primary);
font-weight: 600;
}
td code {
font-size: 12px;
font-weight: 500;
}
/* 空状态 */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--text-secondary);
}
.empty-state-icon {
font-size: 48px;
color: var(--text-tertiary);
margin-bottom: 16px;
}
.empty-state-text {
font-size: 14px;
color: var(--text-secondary);
}
/* 响应式表格优化 */
@media (max-width: 1200px) {
.table-container {
overflow-x: auto;
}
table {
min-width: 1000px;
}
}
@media (max-width: 768px) {
.card-body {
padding: 16px;
}
th, td {
padding: 12px 16px;
}
.action-buttons {
flex-direction: column;
gap: 6px;
}
.action-btn {
width: 100%;
justify-content: center;
}
}
/* 卡片样式 */
.card {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow-sm);
margin-bottom: 24px;
border: 1px solid var(--border-color);
}
.card-header {
padding: 18px 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
background: var(--bg-color);
}
.card-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
letter-spacing: -0.2px;
}
.card-body {
padding: 20px 16px;
}
/* 按钮样式 */
.btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(27, 100, 243, 0.3);
}
.btn-secondary {
background: #f0f0f0;
color: var(--text-light);
}
.btn-secondary:hover {
background: #e0e0e0;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-success:hover {
background: #389e0d;
}
.btn-danger {
background: var(--error-color);
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
/* 标签页样式 */
.tabs {
display: flex;
gap: 16px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.tab {
padding: 10px 0;
color: var(--text-light);
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.3s ease;
font-size: 14px;
}
.tab:hover {
color: var(--primary-color);
}
.tab.active {
color: var(--primary-color);
border-bottom-color: var(--primary-color);
}
/* 表格样式 */
.table-container {
overflow-x: auto;
border-radius: 8px;
width: 100%;
}
table {
width: 100%;
border-collapse: collapse;
background: var(--card-bg);
}
th, td {
padding: 16px 20px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
font-size: 13px;
text-transform: uppercase;
letter-spacing: 0.5px;
white-space: nowrap;
}
tbody tr {
transition: all 0.2s;
}
tbody tr:hover {
background: rgba(27, 100, 243, 0.04);
}
tbody tr:last-child td {
border-bottom: none;
}
td {
color: var(--text-primary);
font-size: 14px;
vertical-align: middle;
}
.status {
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
}
.status.active {
background: #f6ffed;
color: var(--success-color);
}
.status.inactive {
background: #fff2e8;
color: var(--warning-color);
}
.action-buttons {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.action-btn {
padding: 6px 14px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.action-btn i {
font-size: 12px;
}
.action-btn.edit-btn {
background: rgba(27, 100, 243, 0.1);
color: var(--primary-color);
}
.action-btn.edit-btn:hover {
background: rgba(27, 100, 243, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.delete-btn {
background: rgba(239, 68, 68, 0.1);
color: var(--error-color);
}
.action-btn.delete-btn:hover {
background: rgba(239, 68, 68, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(239, 68, 68, 0.2);
}
.action-btn.view-btn {
background: rgba(27, 100, 243, 0.08);
color: var(--primary-color);
}
.action-btn.view-btn:hover {
background: rgba(27, 100, 243, 0.15);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(27, 100, 243, 0.2);
}
.action-btn.copy-btn {
background: rgba(34, 197, 94, 0.1);
color: var(--success-color);
}
.action-btn.copy-btn:hover {
background: rgba(34, 197, 94, 0.2);
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(34, 197, 94, 0.2);
}
/* 模态框样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 2000;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 600px;
max-height: 80vh;
overflow-y: auto;
}
.add-modal-content {
background: white;
border-radius: 8px;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
width: 90%;
max-width: 800px;
max-height: 85vh;
overflow-y: auto;
}
.modal-header {
padding: 16px 24px;
border-bottom: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: space-between;
}
.modal-title {
font-size: 16px;
font-weight: 600;
color: var(--text-color);
}
.close-modal {
background: none;
border: none;
font-size: 20px;
color: var(--text-light);
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
transition: all 0.3s;
}
.close-modal:hover {
background: var(--secondary-color);
color: var(--text-color);
}
.modal-body {
padding: 24px;
}
.modal-footer {
padding: 16px 24px;
border-top: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: flex-end;
gap: 12px;
}
/* 新增表单模态框样式 */
.add-modal-form .form-group {
margin-bottom: 20px;
}
.add-modal-form .form-row {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.add-modal-form .form-row .form-group {
flex: 1;
margin-bottom: 0;
}
.add-modal-form label {
display: block;
margin-bottom: 6px;
font-weight: 500;
color: var(--text-secondary);
font-size: 14px;
}
.add-modal-form input,
.add-modal-form select,
.add-modal-form textarea {
width: 100%;
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
transition: border-color 0.3s;
}
.add-modal-form input:focus,
.add-modal-form select:focus,
.add-modal-form textarea:focus {
outline: none;
border-color: var(--primary-color);
}
.add-modal-form .section-title {
font-size: 16px;
font-weight: 600;
color: var(--text-primary);
margin: 20px 0 15px 0;
padding-bottom: 8px;
border-bottom: 1px solid var(--border-color);
}
.upload-box {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
background-color: var(--bg-color);
}
.upload-box:hover {
border-color: var(--primary-color);
background-color: rgba(27, 100, 243, 0.05);
}
.upload-icon {
font-size: 24px;
color: var(--primary-color);
margin-bottom: 10px;
}
.upload-info {
color: var(--text-secondary);
font-size: 13px;
}
.file-name {
margin-top: 10px;
font-weight: 500;
color: var(--primary-color);
font-size: 13px;
}
.param-table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
.param-table th, .param-table td {
border: 1px solid var(--border-color);
padding: 8px;
text-align: left;
}
.param-table th {
background-color: var(--bg-color);
font-weight: 600;
}
.add-param-btn {
background: var(--primary-color);
color: white;
border: none;
padding: 8px 15px;
border-radius: 6px;
cursor: pointer;
margin-top: 10px;
font-size: 14px;
}
.add-param-btn:hover {
background: var(--primary-hover);
}
/* 二维码预览样式 */
.qrcode-preview-large {
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background: var(--primary-light);
border-radius: 8px;
margin-bottom: 20px;
}
.qrcode-image {
width: 200px;
height: 200px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 15px;
}
.qrcode-image img {
max-width: 100%;
max-height: 100%;
}
/* 详情信息样式 */
.detail-info {
margin-top: 20px;
}
.detail-item {
display: flex;
margin-bottom: 12px;
font-size: 14px;
}
.detail-label {
width: 100px;
color: var(--text-light);
flex-shrink: 0;
}
.detail-value {
color: var(--text-color);
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
}
.sidebar.show-mobile {
transform: translateX(0);
}
.main-content {
margin-left: 0;
margin-top: 0;
padding: 16px; /* 在移动端保持较小的内边距 */
}
.header, .top-header {
left: 0;
}
.tabs {
flex-wrap: wrap;
}
}
/* 批量上传相关样式 */
.batch-upload-modal .modal-content {
max-width: 800px;
}
.template-selector {
margin-bottom: 20px;
}
.template-selector label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: var(--text-primary);
}
.template-selector select {
width: 100%;
padding: 10px 15px;
border: 1px solid var(--border-color);
border-radius: 6px;
font-size: 14px;
background: white;
}
.template-download-btn {
margin-top: 10px;
padding: 8px 16px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.template-download-btn:hover {
background: var(--primary-hover);
}
.upload-area {
border: 2px dashed var(--border-color);
border-radius: 8px;
padding: 40px 20px;
text-align: center;
margin-bottom: 20px;
cursor: pointer;
transition: all 0.3s;
background: var(--bg-color);
}
.upload-area:hover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.05);
}
.upload-area.dragover {
border-color: var(--primary-color);
background: rgba(27, 100, 243, 0.1);
}
.upload-icon-large {
font-size: 48px;
color: var(--primary-color);
margin-bottom: 12px;
}
.upload-text {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 8px;
}
.upload-hint {
font-size: 12px;
color: var(--text-tertiary);
}
.progress-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid var(--border-color);
}
.progress-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
font-size: 14px;
}
.progress-stats {
display: flex;
gap: 20px;
}
.stat-item {
display: flex;
align-items: center;
gap: 6px;
}
.stat-label {
color: var(--text-secondary);
}
.stat-value {
font-weight: 600;
color: var(--text-primary);
}
.stat-value.success {
color: var(--success-color);
}
.stat-value.error {
color: var(--error-color);
}
.progress-bar-container {
width: 100%;
height: 8px;
background: var(--bg-color);
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: var(--primary-color);
transition: width 0.3s ease;
border-radius: 4px;
}
.error-list {
margin-top: 16px;
max-height: 200px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: 6px;
padding: 12px;
background: #fff5f5;
}
.error-item {
padding: 8px;
margin-bottom: 6px;
background: white;
border-left: 3px solid var(--error-color);
border-radius: 4px;
font-size: 13px;
}
.error-item:last-child {
margin-bottom: 0;
}
.error-row {
display: flex;
justify-content: space-between;
align-items: center;
}
.error-reason {
color: var(--error-color);
font-weight: 500;
}
.download-error-btn {
margin-top: 12px;
padding: 8px 16px;
background: var(--error-color);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 13px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.download-error-btn:hover {
background: #cf1322;
}
.validation-table {
width: 100%;
border-collapse: collapse;
margin-top: 16px;
font-size: 13px;
table-layout: fixed;
}
.validation-table th,
.validation-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid var(--border-color);
white-space: normal;
word-break: normal;
overflow: visible;
vertical-align: middle;
}
.validation-table th {
background: var(--bg-color);
font-weight: 600;
color: var(--text-secondary);
white-space: nowrap;
}
.validation-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.validation-table td:nth-child(5) {
white-space: normal;
word-break: break-word;
}
.validation-table th:nth-child(1),
.validation-table td:nth-child(1) {
width: 60px;
min-width: 60px;
text-align: center;
}
.validation-table th:nth-child(2),
.validation-table td:nth-child(2) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(3),
.validation-table td:nth-child(3) {
width: 120px;
min-width: 120px;
}
.validation-table th:nth-child(4),
.validation-table td:nth-child(4) {
width: 100px;
min-width: 100px;
text-align: center;
}
.validation-table th:nth-child(5),
.validation-table td:nth-child(5) {
width: auto;
min-width: 200px;
}
.validation-table tr.error-row {
background: #fff5f5;
}
.validation-table tr.error-row td {
color: var(--error-color);
}
#validation-table-container {
overflow-x: auto;
max-width: 100%;
width: 100%;
}
#validation-table-container table {
min-width: 600px;
}
.file-name-display {
margin-top: 12px;
padding: 10px;
background: var(--bg-color);
border-radius: 6px;
font-size: 13px;
color: var(--text-secondary);
}
table {
width: 100%;
border-collapse: collapse;
}
th {
background-color: #01337a;
color: #fff;
padding: 10px;
text-align: left;
border: 1px solid #ddd;
}
td {
padding: 10px;
border: 1px solid #ddd;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 在主页面CSS中添加 */
.sidebar-iframe {
position: fixed;
left: 0;
top: 0;
width: var(--sidebar-width);
height: 100vh;
border: none;
z-index: 999;
}
/* 为main-content和header添加左边距以避开iframe */
.main-content {
margin-left: var(--sidebar-width);
margin-top: var(--header-height);
}
.top-header {
left: var(--sidebar-width);
}
</style>
<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 class="app-container">
<div id="sidebar-container"></div>
<header class="top-header">
<div class="header-left">
<div class="breadcrumb">
<span>首页</span>
<span class="breadcrumb-separator">/</span>
<span class="breadcrumb-current">设备档案</span>
</div>
</div>
<div class="header-right">
<button class="header-icon-btn" title="通知">
<svg class="nav-icon" viewBox="0 0 20 20">
<path d="M10 2a6 6 0 00-6 6v3.586l-.707.707A1 1 0 004 14h12a1 1 0 00.707-1.707L16 11.586V8a6 6 0 00-6-6zM10 18a3 3 0 01-3-3h6a3 3 0 01-3 3z"/>
</svg>
<span class="notification-badge"></span>
</button>
<div class="lang-switcher">
<button class="lang-btn active">中文</button>
<button class="lang-btn">EN</button>
</div>
<div class="user-avatar"></div>
</div>
</header>
<div class="main-content" style="position: relative;">
<div id="app">
<div class="container" style="margin: 0;">
<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 {
quoteNo: quoteNo,
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: ''
},
laborDetails: [],
partDetails: []
}
},
mounted() {
this.loadQuoteData();
},
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: {
loadQuoteData() {
// 从localStorage加载报价单数据
const saved = localStorage.getItem('serviceQuotes');
if (saved) {
const quotes = JSON.parse(saved);
const quote = quotes.find(q => q.quoteNo === this.quoteNo);
if (quote) {
this.quoteInfo = {
quoteNo: quote.quoteNo,
customerName: quote.customerName,
serviceDescription: quote.serviceDescription,
quoteDate: quote.quoteDate,
validUntil: quote.validUntil,
status: quote.status,
whitebookVersion: quote.whitebookVersion || 'V1.0',
ruleName: quote.ruleName || '',
scenario: quote.scenario || '',
remark: quote.remark || '本报价单有效期30天,最终价格以实际服务为准。'
};
this.laborDetails = quote.laborDetails || [];
this.partDetails = quote.partDetails || [];
}
}
},
handleExport() {
alert('导出功能(占位)\n将导出为PDF或Excel格式');
},
handlePrint() {
window.print();
},
handleBack() {
window.location.href = '11-服务单列表.html';
}
}
}).mount('#app');
</script>
</div>
</div>
</body>
<script src="./js.js"></script>
</html>
\ No newline at end of file
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