Zzc 677a5fa9ac feat(screen): 添加可编辑的计划步骤和基于站点的调度计划
- 使用el-input将静态计划步骤和后续文本转换为可编辑输入框
- 使清场步骤和调度计划具有响应性,支持动态站点分配
- 添加用于添加和删除调度计划的UI元素(当前已禁用)
- 为新的交互组件更新样式并改善布局
- 从父组件集成stations属性以实现站点名称的自动分配
2025-11-21 18:16:29 +08:00

984 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="emergency-plan-content">
<!-- 1. 现场指挥部 -->
<section class="plan-section">
<div class="section-header">
<img
src="../../assets/images/modal/弹窗title.png"
alt=""
class="title-icon"
/>
<h3 class="section-title">现场指挥部</h3>
</div>
<div class="section-body">
<div class="form-grid">
<div class="form-item">
<label>指挥长</label>
<el-select
v-model="formData.commander"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="王军" value="王军" />
<el-option label="李明" value="李明" />
<el-option label="张伟" value="张伟" />
</el-select>
</div>
<div class="form-item">
<label>副指挥长</label>
<el-select
v-model="formData.viceCommander"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="刘勇" value="刘勇" />
<el-option label="陈强" value="陈强" />
<el-option label="赵军" value="赵军" />
</el-select>
</div>
</div>
</div>
</section>
<!-- 2. 多路协同处置三级指挥中心 -->
<section class="plan-section">
<div class="section-header">
<img
src="../../assets/images/modal/弹窗title.png"
alt=""
class="title-icon"
/>
<h3 class="section-title">多路协同处置三级指挥中心</h3>
</div>
<div class="section-body">
<div class="form-grid grid-2x2">
<div class="form-item">
<label>交通管控</label>
<el-select
v-model="formData.trafficControl"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="公安交警、交通执法队" value="公安交警、交通执法队" />
<el-option label="交通管理局" value="交通管理局" />
</el-select>
</div>
<div class="form-item">
<label>交通信息发布</label>
<el-select
v-model="formData.infoRelease"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="融媒体中心" value="融媒体中心" />
<el-option label="新闻中心" value="新闻中心" />
</el-select>
</div>
<div class="form-item">
<label>人车调拨</label>
<el-select
v-model="formData.vehicleDispatch"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="xxx消防队" value="xxx消防队" />
<el-option label="应急救援队" value="应急救援队" />
</el-select>
</div>
<div class="form-item">
<label>人员救援</label>
<el-select
v-model="formData.personnelRescue"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="xx医院" value="xx医院" />
<el-option label="中心医院" value="中心医院" />
</el-select>
</div>
</div>
</div>
</section>
<!-- 3. 公路抢通方案 -->
<section class="plan-section">
<div class="section-header">
<img
src="../../assets/images/modal/弹窗title.png"
alt=""
class="title-icon"
/>
<h3 class="section-title">公路抢通方案</h3>
</div>
<div class="section-body">
<div class="form-grid grid-2-cols">
<div class="form-item">
<label>抢通方式</label>
<el-select
v-model="formData.clearanceMethod"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option
label="两端对接抢通,必要时开辟工作面"
value="两端对接抢通,必要时开辟工作面"
/>
<el-option label="单端推进抢通" value="单端推进抢通" />
<el-option label="多点同时作业" value="多点同时作业" />
</el-select>
</div>
<div class="form-item">
<label>预计抢通时间</label>
<el-select
v-model="formData.estimatedTime"
class="custom-select"
popper-class="custom-dropdown"
>
<el-option label="6小时" value="6小时" />
<el-option label="8小时" value="8小时" />
<el-option label="12小时" value="12小时" />
<el-option label="24小时" value="24小时" />
</el-select>
</div>
</div>
<div class="plan-steps">
<div v-for="step in clearanceSteps" :key="step.id" class="step-item">
<label class="step-label">{{ step.number }}{{ step.title }}</label>
<el-input
v-model="step.content"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
class="step-content-input"
/>
</div>
</div>
</div>
</section>
<!-- 4. 力量调派方案 -->
<section class="plan-section">
<div class="section-header">
<img
src="../../assets/images/modal/弹窗title.png"
alt=""
class="title-icon"
/>
<h3 class="section-title">力量调派方案</h3>
<button class="add-btn" @click="handleAddPlan">
<span class="add-icon">+</span>
<span>新增</span>
</button>
</div>
<div class="section-body">
<div class="dispatch-plans">
<div
v-for="plan in dispatchPlans"
:key="plan.id"
class="dispatch-card"
>
<button class="close-btn" @click="handleDeletePlan(plan.id)" title="删除">×</button>
<h4 class="plan-name">
<span class="plan-icon"></span>
基地{{ plan.id }}{{ plan.stationName || 'xxxxx名称' }}
</h4>
<div class="resource-grid">
<div
v-for="(resource, index) in plan.resources"
:key="index"
class="resource-item"
>
<span class="resource-label">{{ resource.label }}</span>
<span class="resource-value">{{ resource.value }}</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 5. 后续处治 -->
<section class="plan-section">
<div class="section-header">
<img
src="../../assets/images/modal/弹窗title.png"
alt=""
class="title-icon"
/>
<h3 class="section-title">后续处治</h3>
</div>
<div class="section-body">
<el-input
v-model="followUpText"
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
class="follow-up-textarea"
/>
</div>
</section>
</div>
</template>
<script setup>
import { ref, reactive, computed, watch } from "vue";
/**
* Props 定义
*/
const props = defineProps({
stations: {
type: Array,
default: () => [],
},
});
/**
* 可用站点列表(从快速匹配传入)
*/
const availableStations = computed(() => props.stations);
/**
* 表单数据
*/
const formData = reactive({
commander: "王军",
viceCommander: "刘勇",
trafficControl: "公安交警、交通执法队",
infoRelease: "融媒体中心",
vehicleDispatch: "xxx消防队",
personnelRescue: "xx医院",
clearanceMethod: "两端对接抢通,必要时开辟工作面",
estimatedTime: "6小时",
});
/**
* 公路抢通方案步骤
*/
const clearanceSteps = reactive([
{
id: 1,
number: "①",
title: "评估封控",
content:
"对现场进行封控,清点各类应急物资提前到达现场,划定作业区与危险区,设立观察哨,全程监控边坡状况。",
},
{
id: 2,
number: "②",
title: "边坡排险",
content:
"在确保安全的前提下,使用长臂挖掘机或人工在安全地域进行清除工作。注意下方施救作业。",
},
{
id: 3,
number: "③",
title: "梯形清方",
content:
'按照机械自上而下,装载机抢先挖入土石堆入车,确土车上车主生,增土车队运输,将受阻路段疏通清理通道。确保抢险不停工,形成高效的"挖一装一运"循环。',
},
{
id: 4,
number: "④",
title: "开辟工作面",
content:
"根据地质情况而规划车,利用挖掘机、装载机完成设备增加的工作面。增加作业效率。",
},
{
id: 5,
number: "⑤",
title: "交通疏导",
content:
"清理出足够疏散车辆的临时性通道,临时道路根据道路情况设置限量所制路牌。在应急抢险下,试行错位通车。",
},
{
id: 6,
number: "⑥",
title: "全断面抢通",
content: "将剩余集聚废弃物的清理清除,恢复道路原貌状。",
},
]);
/**
* 力量调派预案
*/
const dispatchPlans = ref([
{
id: 1,
stationName: "", // 站点名称,从 stations 自动赋值
resources: [
{ label: "轮播挖掘机挖掘机", value: "1台" },
{ label: "平板拖车", value: "1台" },
{ label: "自卸货车", value: "4台" },
{ label: "工程车", value: "1台" },
{ label: "人员", value: "15人" },
{ label: "标志标牌", value: "5块" },
{ label: "铁锹", value: "30件" },
{ label: "铁镐", value: "10件" },
{ label: "麻袋、砂石袋等", value: "若干" },
],
},
{
id: 2,
stationName: "",
resources: [
{ label: "轮播挖掘机挖掘机", value: "1台" },
{ label: "平板拖车", value: "1台" },
{ label: "自卸货车", value: "4台" },
{ label: "工程车", value: "1台" },
{ label: "人员", value: "15人" },
{ label: "标志标牌", value: "5块" },
{ label: "铁锹", value: "30件" },
{ label: "铁镐", value: "10件" },
{ label: "麻袋、砂石袋等", value: "若干" },
],
},
]);
/**
* 后续处治文本
*/
const followUpText = ref(
'将该处于方案大部分障碍清点,设置"注意落石"等标志牌,进行巡逻管控,进行巡逻处理信号监测统设备。'
);
/**
* 根据站点索引获取站点名称
* @param {number} index - 站点索引从0开始
* @returns {string} 站点名称
*/
const getStationNameByIndex = (index) => {
if (availableStations.value && availableStations.value.length > index) {
return availableStations.value[index].name;
}
return "";
};
/**
* 自动为所有基地分配站点名称
*/
const assignStationNames = () => {
dispatchPlans.value.forEach((plan, index) => {
plan.stationName = getStationNameByIndex(index);
});
};
/**
* 监听站点数据变化,自动分配站点名称
*/
watch(
() => props.stations,
(newStations) => {
console.log("[EmergencyPlanContent] 站点数据变化检测:", newStations);
if (newStations && newStations.length > 0) {
assignStationNames();
console.log("[EmergencyPlanContent] 站点数据更新,自动分配站点名称");
console.log("[EmergencyPlanContent] 当前基地列表:", dispatchPlans.value);
} else {
console.log("[EmergencyPlanContent] 站点数据为空或未定义");
}
},
{ immediate: true, deep: true }
);
/**
* 处理新增预案按钮点击
*/
const handleAddPlan = () => {
// 暂时禁止新增预案
return
console.log("[EmergencyPlanContent] 点击新增预案");
const newId = Math.max(...dispatchPlans.value.map((p) => p.id)) + 1;
const newIndex = dispatchPlans.value.length;
dispatchPlans.value.push({
id: newId,
stationName: getStationNameByIndex(newIndex),
resources: [
{ label: "轮播挖掘机挖掘机", value: "1台" },
{ label: "平板拖车", value: "1台" },
{ label: "自卸货车", value: "4台" },
{ label: "工程车", value: "1台" },
{ label: "人员", value: "15人" },
{ label: "标志标牌", value: "5块" },
{ label: "铁锹", value: "30件" },
{ label: "铁镐", value: "10件" },
{ label: "麻袋、砂石袋等", value: "若干" },
],
});
};
/**
* 处理删除预案
*/
const handleDeletePlan = (id) => {
// 暂时禁止删除预案
return
console.log("[EmergencyPlanContent] 删除预案", id);
const index = dispatchPlans.value.findIndex((p) => p.id === id);
if (index > -1) {
dispatchPlans.value.splice(index, 1);
// 删除后重新分配站点名称,保持顺序
assignStationNames();
}
};
</script>
<style scoped lang="scss">
@use "@/styles/mixins.scss" as *;
.emergency-plan-content {
padding: 0;
color: var(--text-white);
}
/* 区块样式 */
.plan-section {
margin-bottom: vh(16);
// border: 1px solid rgba(28, 161, 255, 0.3);
border-radius: vw(4);
overflow: hidden;
&:last-child {
margin-bottom: 0;
}
}
/* 区块头部 */
.section-header {
display: flex;
align-items: center;
gap: vw(8);
padding: vh(2) vw(18);
background: #1f497a;
.title-icon {
width: vw(8);
height: vh(32);
object-fit: contain;
}
.section-title {
font-size: fs(18);
font-family: SourceHanSansCN-Bold, sans-serif;
font-weight: bold;
color: var(--text-white);
margin: 0;
}
.add-btn {
margin-left: auto;
padding: vh(6) vw(16);
background: var(--primary-color);
border: none;
border-radius: vw(4);
color: var(--text-white);
font-size: fs(14);
font-family: SourceHanSansCN-Medium, sans-serif;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: vw(6);
.add-icon {
font-size: fs(18);
font-weight: bold;
line-height: 1;
}
&:hover {
background: var(--primary-light);
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
}
}
/* 区块内容 */
.section-body {
padding: vh(14) vw(18);
}
/* 表单网格 */
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: vw(16) vh(12);
&.grid-2x2 {
grid-template-columns: repeat(2, 1fr);
}
&.grid-2-cols {
grid-template-columns: 1fr 1fr;
}
}
/* 表单项 */
.form-item {
display: flex;
align-items: center;
gap: vw(10);
label {
width: vw(100);
font-size: fs(14);
font-family: SourceHanSansCN-Regular, sans-serif;
color: var(--text-gray);
white-space: nowrap;
flex-shrink: 0;
}
// Element Plus el-select 自定义样式
:deep(.custom-select) {
width: 100%;
background: #052044 !important;
// 选择器容器
.el-select__wrapper {
background: #052044 !important;
box-shadow: none !important;
}
// 输入框包装器
.el-input {
background: #052044 !important;
&__wrapper {
background-color: #052044 !important;
background: #052044 !important;
border: 1px solid rgba(28, 161, 255, 0.3) !important;
border-radius: vw(4);
box-shadow: none !important;
padding: vh(6) vw(12);
transition: none !important;
&:hover {
border-color: var(--primary-color) !important;
background-color: #052044 !important;
background: #052044 !important;
}
&.is-focus,
&.is-focused {
border-color: var(--primary-color) !important;
box-shadow: none !important;
background-color: #052044 !important;
background: #052044 !important;
}
}
&__inner {
color: #fff !important;
font-size: fs(13);
font-family: SourceHanSansCN-Regular, sans-serif;
height: auto;
background-color: transparent !important;
background: transparent !important;
// 强制覆盖 placeholder 颜色,确保选中值为纯白色
&:not(:placeholder-shown) {
color: #fff !important;
opacity: 1 !important;
}
}
}
// 选择器内部的输入框
.el-input {
color: #fff !important;
opacity: 1 !important;
&__inner {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
// 覆盖所有可能的状态
&[readonly] {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
&:disabled {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
}
// 输入框后缀区域
&__suffix {
background-color: transparent !important;
background: transparent !important;
}
// 输入框前缀区域
&__prefix {
background-color: transparent !important;
background: transparent !important;
}
}
// 输入框文本
.el-select__input {
color: #fff !important;
opacity: 1 !important;
}
// 选中的值文本(优先级最高,放在最前)
.el-select__selected-item {
color: #fff !important;
opacity: 1 !important;
}
// placeholder 文本(只针对真正的 placeholder
.el-input__inner::placeholder {
color: rgba(255, 255, 255, 0.5) !important;
}
// 只在没有选中值时显示半透明的 placeholder
.el-select__placeholder.is-transparent {
color: rgba(255, 255, 255, 0.5) !important;
}
// 强制覆盖 input 元素的文本颜色(针对 readonly 状态)
input.el-input__inner {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
// 下拉箭头颜色
.el-select__caret {
color: var(--text-gray) !important;
}
// 清除按钮颜色
.el-select__clear {
color: var(--text-gray) !important;
}
// 选中的标签(多选时)
.el-select__tags {
background: transparent !important;
}
}
}
/* 方案步骤 */
.plan-steps {
margin-top: vh(16);
display: flex;
flex-direction: column;
gap: vh(12);
}
.step-item {
display: flex;
align-items: center;
gap: vw(10);
.step-label {
width: vw(100);
font-size: fs(14);
font-family: SourceHanSansCN-Regular, sans-serif;
color: var(--text-gray);
white-space: nowrap;
flex-shrink: 0;
}
.step-content {
flex: 1;
font-size: fs(13);
font-family: SourceHanSansCN-Regular, sans-serif;
line-height: 1.6;
color: var(--text-white);
padding: vh(8) vw(12);
background: #052044;
border-radius: vw(4);
}
.step-content-input {
flex: 1;
:deep(.el-textarea__inner) {
background: #052044 !important;
border: 1px solid rgba(28, 161, 255, 0.3) !important;
border-radius: vw(4);
color: var(--text-white);
font-size: fs(13);
font-family: SourceHanSansCN-Regular, sans-serif;
line-height: 1.6;
padding: vh(8) vw(12);
transition: border-color 0.3s ease;
&:hover {
border-color: var(--primary-color) !important;
}
&:focus {
border-color: var(--primary-color) !important;
box-shadow: none !important;
}
&::placeholder {
color: rgba(255, 255, 255, 0.3);
}
}
}
}
/* 调派预案 */
.dispatch-plans {
display: flex;
flex-direction: column;
gap: vh(12);
}
.dispatch-card {
background: rgba(255, 255, 255, 0.06);
border: 1px solid rgba(28, 161, 255, 0.2);
border-radius: vw(8);
padding: vh(12) vw(14);
position: relative;
.close-btn {
position: absolute;
top: vh(12);
right: vw(14);
width: vw(24);
height: vw(24);
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.3);
background: transparent;
color: rgba(255, 255, 255, 0.6);
font-size: fs(20);
line-height: 1;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
&:hover {
background: rgba(255, 77, 79, 0.2);
border-color: #ff4d4f;
color: #ff4d4f;
transform: rotate(90deg);
}
&:active {
transform: rotate(90deg) scale(0.95);
}
}
.plan-name {
font-size: fs(15);
font-family: SourceHanSansCN-Bold, sans-serif;
font-weight: bold;
color: var(--text-white);
margin: 0 vw(30) vh(10) 0;
.plan-icon {
display: inline-block;
width: 9px;
height: 9px;
border: 2px solid #4fecff;
border-radius: 50%;
margin-right: 4px;
}
}
.resource-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: vw(8) vh(8);
}
.resource-item {
display: flex;
// flex-direction: column;
gap: vh(2);
.resource-label {
font-size: fs(12);
font-family: SourceHanSansCN-Regular, sans-serif;
color: var(--text-gray);
width: vw(100);
}
.resource-value {
font-size: fs(13);
font-family: SourceHanSansCN-Medium, sans-serif;
font-weight: 500;
color: var(--text-white);
}
}
}
/* 后续处治文本 */
.follow-up-text {
font-size: fs(13);
font-family: SourceHanSansCN-Regular, sans-serif;
line-height: 1.8;
color: var(--text-white);
margin: 0;
padding: vh(10) vw(14);
background: #052044;
border-radius: vw(4);
}
.follow-up-textarea {
:deep(.el-textarea__inner) {
background: #052044 !important;
border: 1px solid rgba(28, 161, 255, 0.3) !important;
border-radius: vw(4);
color: var(--text-white);
font-size: fs(13);
font-family: SourceHanSansCN-Regular, sans-serif;
line-height: 1.8;
padding: vh(10) vw(14);
transition: border-color 0.3s ease;
&:hover {
border-color: var(--primary-color) !important;
}
&:focus {
border-color: var(--primary-color) !important;
box-shadow: none !important;
}
&::placeholder {
color: rgba(255, 255, 255, 0.3);
}
}
}
</style>
<style lang="scss">
// 全局样式el-select 输入框背景和文字颜色(强制覆盖)
.custom-select {
.el-input__wrapper {
background-color: #052044 !important;
background: #052044 !important;
}
.el-input {
background-color: #052044 !important;
background: #052044 !important;
color: #fff !important;
opacity: 1 !important;
&__inner {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
// 覆盖所有可能的状态
&[readonly] {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
&:disabled {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
// 强制覆盖 placeholder 颜色,确保选中值为纯白色
&:not(:placeholder-shown) {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
}
}
// 输入框文本
.el-select__input {
color: #fff !important;
opacity: 1 !important;
}
// 选中的值(优先级最高,先定义)
.el-select__selected-item {
color: #fff !important;
opacity: 1 !important;
}
// placeholder 文本(只针对真正的 placeholder
.el-input__inner::placeholder {
color: rgba(255, 255, 255, 0.5) !important;
}
// 只在没有选中值时显示半透明的 placeholder
.el-select__placeholder.is-transparent {
color: rgba(255, 255, 255, 0.5) !important;
}
// 强制覆盖 input 元素的文本颜色(针对 readonly 状态)
input.el-input__inner {
color: #fff !important;
opacity: 1 !important;
-webkit-text-fill-color: #fff !important;
}
}
// 全局样式:下拉面板(挂载在 body 上,需要非 scoped 样式)
.custom-dropdown {
background: #052044 !important;
border: 1px solid rgba(28, 161, 255, 0.3) !important;
// 下拉列表容器
.el-select-dropdown__wrap {
background: #052044 !important;
}
// 下拉列表
.el-select-dropdown__list {
background: #052044 !important;
padding: 0;
}
// 下拉选项
.el-select-dropdown__item {
background: #052044 !important;
color: #fff;
font-size: 13px;
font-family: SourceHanSansCN-Regular, sans-serif;
&:hover {
background: rgba(28, 161, 255, 0.2) !important;
}
&.is-selected {
background: rgba(28, 161, 255, 0.3) !important;
color: #fff;
font-weight: 500;
}
&.is-hovering {
background: rgba(28, 161, 255, 0.2) !important;
}
}
// 空状态文字颜色
.el-select-dropdown__empty {
color: rgba(179, 204, 226, 1);
background: #052044 !important;
}
// 滚动条样式
.el-scrollbar__view {
background: #052044 !important;
}
}
</style>