This commit is contained in:
Zzc 2025-11-16 14:39:05 +08:00
commit 972e35f278
19 changed files with 1360 additions and 793 deletions

View File

@ -48,7 +48,7 @@
@close="onPopupClose" @close="onPopupClose"
> >
<van-form class="materialAddForm" label-align="left" colon> <van-form class="materialAddForm" label-align="left" colon>
<h3>设备信息</h3> <h3>添加物资</h3>
<!-- 物资名称 --> <!-- 物资名称 -->
<van-field <van-field

View File

@ -0,0 +1,103 @@
<template>
<div class="info-box">
<div class="info-title-block">
<span class="title-text">{{ title }}</span>
</div>
<div class="info-content-block">
<div class="info-item-list-wrapper">
<div class="info-item" v-for="(item, index) in dataConfig" :key="index">
<template v-if="item.slot">
<slot :item="item.slot" v-bind="item" />
</template>
<template v-else>
<div class="label">{{ getLabelText(item) }}</div>
<div class="value">{{ getValueText(item) }}</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
const props = defineProps({
title: {
type: String,
default: ''
},
data: {
type: Object,
default: () => {}
},
dataConfig: {
type: Array,
default: () => []
}
})
const getLabelText = (item) => {
if(typeof item.label == 'function') {
return item.label(props.data)
}
return item.label;
}
const getValueText = (item) => {
if(typeof item.value == 'function') return item.value(props.data)
return props.data[item.name];
}
</script>
<style scoped lang="scss">
.info-box {
width: 100%;
.info-title-block {
margin: 10px 0;
.title-text {
font-size: 16px;
font-weight: 600;
}
}
.info-content-block {
width: 100%;
padding: 15px;
background-color: #fff;
border-radius: 6px;
.info-item-list-wrapper {
display: grid;
width: 100%;
row-gap: 15px;
.info-item {
display: flex;
align-items: center;
width: 100%;
.label {
display: flex;
align-items: center;
font-size: 14px;
margin-right: 10px;
line-height: 14px;
white-space: nowrap;
&::after {
content: ":"
}
}
.value {
line-height: 14px;
font-size: 14px;
white-space: nowrap;
}
}
}
}
}
</style>

View File

@ -0,0 +1,91 @@
<template>
<div class="dynamic-form">
<el-form class="form-wrapper" :model="modelValue" ref="formRef">
<el-form-item v-for="(config, index) in formConfig" :key="index" :prop="config['prop']"
:label="config['label']" :rules="config['rules']" label-position="right">
<template v-if="config.type == 'input'">
<el-input :modelValue="modelValue[config.name]"
@update:modelValue="(event) => changeValue(config, event)"
:placeholder="config.componentProps?.placeholder" :clearable="true" />
</template>
<template v-if="config.type == 'inputNumber'">
<el-input-number :modelValue="modelValue[config.name]"
@update:modelValue="(event) => changeValue(config, event)"
:placeholder="config.componentProps?.placeholder" :clearable="true" />
</template>
<template v-if="config.type == 'radio'">
<el-radio-group :modelValue="modelValue[config.name]"
@update:modelValue="(event) => changeValue(config, event)"
:placeholder="config.componentProps?.placeholder">
<el-radio v-for="(option, i) in config.options" :value="option.value">{{ option.label
}}</el-radio>
</el-radio-group>
</template>
<template v-if="config.type == 'select'">
<el-select :modelValue="modelValue[config.name]"
@update:modelValue="(event) => changeValue(config, event)"
:placeholder="config.componentProps?.placeholder">
<el-option v-for="(option, i) in config.options" :key="i" :label="option.label"
:value="option.value"></el-option>
</el-select>
</template>
<template v-if="config.type == 'date'">
<el-date-picker :modelValue="modelValue[config.name]" type="date"
@update:modelValue="(event) => changeValue(config, event)"
:placeholder="config.componentProps?.placeholder" />
</template>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { onMounted, watch, ref } from 'vue';
const props = defineProps({
formConfig: {
type: Array,
default: () => []
},
modelValue: {
type: Object,
default: () => { }
}
})
const emit = defineEmits(['update:modelValue'])
const formRef = ref(null)
const changeValue = (config, value) => {
const form = { ...props.modelValue }
form[config.name] = value
emit('update:modelValue', form)
}
//
const getDefaultFormValue = () => {
const form = {}
props.formConfig.forEach(config => {
form[config.name] = config.default !== undefined ? config.default : ''
})
return form
}
defineExpose({
getDefaultFormValue,
formComponent: formRef.value,
})
</script>
<style scoped lang="scss">
.form-wrapper {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
</style>

View File

@ -0,0 +1,172 @@
<template>
<div class="input-selet-comp" ref="inputSelectRef">
<div class="input-wrapper" :class="{ 'is-active': active }">
<div v-if="modelValue == null || modelValue === ''" class="placeholder">{{ placeholder }}</div>
<input :value="modelValue" ref="innerInputRef" class="inner-input" @click="show" @blur="deferClose"
@input="input" />
</div>
</div>
<div class="input-selet-options-popup" ref="popupRef" :style="optionsWrapperStyle"
v-if="options && options.length > 0">
<div class="options-wrapper">
<div class="option" v-for="(option, index) in options" :key="index" @click="changeValue(option)" :class="{'is-select' : option.value == modelValue }">
{{ option.label }}
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
const props = defineProps({
options: {
type: Array,
default: () => []
},
placeholder: {
type: String,
default: ''
},
modelValue: {
}
})
const emit = defineEmits(['update:modelValue']);
const optionsWrapperStyle = ref({})
const inputSelectRef = ref(null)
const innerInputRef = ref(null)
const popupRef = ref(null)
const active = ref(false)
const show = () => {
if (props.options && props.options.length > 0) {
const rect = inputSelectRef.value.getBoundingClientRect();
optionsWrapperStyle.value = {
left: rect.left + 'px',
top: rect.bottom + 'px',
transform: 'scaleY(1)',
width: rect.width + 'px',
}
const popupRect = popupRef.value.getBoundingClientRect();
//
if (rect.right + popupRect.width / 2 > window.innerWidth) {
optionsWrapperStyle.value.left = rect.right - popupRect.width + 'px';
}
active.value = true
}
}
const deferClose = () => {
setTimeout(() => {
close()
}, 100)
}
const close = () => {
if (props.options && props.options.length > 0) {
optionsWrapperStyle.value.transform = 'scaleY(0)';
}
active.value = false
}
const input = (event) => {
emit("update:modelValue", event.target.value)
}
const changeValue = (option) => {
emit("update:modelValue", option.value)
}
</script>
<style scoped lang="scss">
.input-selet-comp {
width: 100%;
.input-wrapper {
position: relative;
background-color: var(--el-input-bg-color, var(--el-fill-color-blank));
background-image: none;
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
padding: 1px 11px;
transform: translateZ(0);
transition: var(--el-transition-box-shadow);
&:hover {
box-shadow: 0 0 0 1px #c0c4cc inset;
}
&.is-active {
box-shadow: 0 0 0 1px #409eff inset;
}
}
.placeholder {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
pointer-events: none;
padding: 1px 11px;
color: var(--el-text-color-placeholder);
}
.inner-input {
width: 100%;
outline: none;
border: none;
background: transparent;
padding: 0;
margin: 0;
}
}
.input-selet-options-popup {
position: fixed;
display: flex;
flex-direction: column;
margin: 0;
z-index: 10;
transform: scaleY(0);
transform-origin: center top;
transition: transform 0.2s;
&::before {
content: '';
display: inline-block;
height: 5px;
}
.options-wrapper {
display: flex;
flex-direction: column;
border-radius: 4px;
padding: 6px 0;
background: var(--el-bg-color-overlay);
border: 1px solid var(--el-border-color-light);
box-shadow: var(--el-box-shadow-light);
}
.option {
border-radius: 4px;
transition: background-color 0.2s;
white-space: nowrap;
color: var(--el-text-color-regular);
cursor: pointer;
font-size: var(--el-font-size-base);
height: 34px;
line-height: 34px;
overflow: hidden;
padding: 0 32px 0 20px;
&.is-select {
color: #409eff;
}
}
}
</style>

View File

@ -10,13 +10,12 @@
:is="dynamicComponent" :is="dynamicComponent"
ref="dynamicComponentRef" ref="dynamicComponentRef"
v-bind="componentProps" v-bind="componentProps"
@vue:mounted="handleComponentMount"
/> />
<slot></slot> <slot></slot>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
<el-button @click="onCancel"> {{ onCancelName }} </el-button> <el-button class="button" size="large" type="primary" @click="onConfirm"> {{ onConfirmName }} </el-button>
<el-button type="primary" @click="onConfirm"> {{ onConfirmName }} </el-button> <el-button class="button" size="large" @click="onCancel"> {{ onCancelName }} </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
@ -60,7 +59,7 @@ const props = defineProps({
}, },
onConfirmName: { onConfirmName: {
type: String, type: String,
default: "确认", default: "保存",
}, },
onCancelName: { onCancelName: {
type: String, type: String,
@ -73,5 +72,13 @@ const normalizedComponent = computed(() =>
); );
</script> </script>
<style> <style lang="scss" scoped>
.dialog-footer {
display: flex;
justify-content: center;
.button {
width: 150px;
}
}
</style> </style>

View File

@ -11,7 +11,6 @@
:is="dynamicComponent" :is="dynamicComponent"
ref="dynamicComponentRef" ref="dynamicComponentRef"
v-bind="componentProps" v-bind="componentProps"
@vue:mounted="handleComponentMount"
/> />
<template #footer> <template #footer>

View File

@ -1,81 +1,98 @@
<template> <template>
<div class="detail-container"> <div>
<el-form <div class="area-title-block">
ref="formRef" 所属服务站: <span class="title-text">{{ stationName }}</span>
label-position="right" </div>
label-width="auto" <el-form :model="form" class="equipment-form">
style="max-height: 60vh; overflow-y: auto; padding-right: 50px" <!-- 第一行设备名称设备大类设备类型 -->
>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="8">
<el-form-item label="操作员:"> <el-form-item label="设备名称" prop="sbmc">
<el-input v-model="form.czy" /> <el-input v-model="form.sbmc" maxlength="20" placeholder="请输入设备名称" />
</el-form-item>
<el-form-item label="辐射范围:">
<el-input v-model="form.fsfw" />
</el-form-item>
<el-form-item label="管理人员:">
<el-input v-model="form.glry" />
</el-form-item>
<el-form-item label="购买费用(万元):">
<el-input v-model="form.gmfy" />
</el-form-item>
<el-form-item label="购置日期:">
<el-input v-model="form.gzrq" />
</el-form-item>
<el-form-item label="区县名称:">
<el-input v-model="form.qxmc" />
</el-form-item>
<el-form-item label="设备编号:">
<el-input v-model="form.sbbh" />
</el-form-item>
<el-form-item label="设备大类:">
<el-input v-model="form.sbdl" />
</el-form-item>
<el-form-item label="设备小类:">
<el-input v-model="form.sbxl" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="8">
<el-form-item label="设备名称:"> <el-form-item label="设备大类" prop="sbdl">
<el-input v-model="form.sbmc" /> <InputSelect v-model="form.sbdl" placeholder="请选择" :options="options['sbdl']" />
</el-form-item> </el-form-item>
<el-form-item label="设备位置:"> </el-col>
<el-input v-model="form.sbwz" /> <el-col :span="8">
<el-form-item label="设备类型" prop="sblx">
<InputSelect v-model="form.sblx" placeholder="请选择" :options="options['sblx']" />
</el-form-item> </el-form-item>
<el-form-item label="设备经度:"> </el-col>
<el-input v-model="form.jd" /> </el-row>
<!-- 第二行设备型号设备经度只读设备纬度只读 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="设备型号" prop="sbxh">
<el-input v-model="form.sbxh" placeholder="请输入设备型号" />
</el-form-item> </el-form-item>
<el-form-item label="设备纬度:"> </el-col>
<el-input v-model="form.wd" /> <el-col :span="8">
<el-form-item label="设备经度" prop="jd">
<el-input v-model="form.jd" placeholder="自动填充服务站经纬度" />
</el-form-item> </el-form-item>
<el-form-item label="设备型号:"> </el-col>
<el-input v-model="form.sbxh" /> <el-col :span="8">
<el-form-item label="设备纬度" prop="wd">
<el-input v-model="form.wd" placeholder="自动填充服务站经纬度" />
</el-form-item> </el-form-item>
<el-form-item label="设备状态:"> </el-col>
<el-select v-model="form.sbzt" placeholder="请选择" clearable> </el-row>
<el-option label="完好" value="完好" />
<el-option label="损坏" value="损坏" /> <!-- 第三行购置日期购买费用万元应急设备 -->
<el-option label="报废" value="报废" /> <el-row :gutter="20">
<el-col :span="8">
<el-form-item label="购置日期" prop="gzrq">
<el-date-picker v-model="form.gzrq" type="date" placeholder="请选择购置日期" style="width: 100%;" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="购买费用(万元)" prop="gmfy">
<el-input-number v-model="form.gmfy" :min="0" controls-position="right" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="应急设备" prop="sfyjsb">
<el-select v-model="form.sfyjsb" placeholder="请选择">
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="生产厂家:"> </el-col>
<el-input v-model="form.sccj" /> </el-row>
<!-- 第四行辐射范围生产商纳入市级补助范围 -->
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="辐射范围" prop="fsfw">
<el-input v-model="form.fsfw" placeholder="请输入辐射范围" />
</el-form-item> </el-form-item>
<el-form-item label="是否应急设备:"> </el-col>
<el-input v-model="form.sfyjsb" /> <el-col :span="8">
<el-form-item label="生产商" prop="sccj">
<el-input v-model="form.sccj" placeholder="请输入生产商" />
</el-form-item> </el-form-item>
<el-form-item label="是否纳入市级补助范围:"> </el-col>
<el-input v-model="form.sfnrsjbz" /> <el-col :span="8">
<el-form-item label="纳入市级补助范围" prop="sfnrsjbz">
<el-select v-model="form.sfnrsjbz" placeholder="请选择">
<el-option label="是" value="是" />
<el-option label="否" value="否" />
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref } from "vue"; import { ref, computed } from "vue";
import InputSelect from "@/component/InputSelect/InputSelect.vue";
const props = defineProps({ const props = defineProps({
detailData: { detailData: {
@ -86,8 +103,43 @@ const props = defineProps({
type: Object, type: Object,
default: () => ({}), default: () => ({}),
}, },
//
yhzData: {
type: [Object, null],
default: null,
}
}); });
const options = ref({
'sbdl': [
{ label: "示例大类1", value: "示例大类1" },
{ label: "示例大类2", value: "示例大类2" },
],
"sblx": [
{ label: "示例类型1", value: "示例类型1" },
{ label: "示例类型2", value: "示例类型2" },
]
})
const stationName = computed(() => {
return props.yhzData ? props.yhzData.rawName : ''
})
</script> </script>
<style> <style scoped lang="scss">
.area-title-block {
margin-bottom: 20px;
.title-text {
color: rgb(10, 124, 255);
font-size: 16px;
}
}
:deep(.equipment-form) {
.el-form-item {
margin-bottom: 30px;
}
}
</style> </style>

View File

@ -1,82 +1,204 @@
<template> <template>
<div class="detail-container"> <div>
<el-form <el-drawer v-model="visible" size="700px" :show-close="false">
label-position="right" <template #header="{ close, titleId, titleClass }">
label-width="auto" <div class="drawer-header">
style="max-height: 60vh; overflow-y: auto; padding-right: 50px" <el-icon class="el-icon--close" @click="close">
> <Close />
<el-row :gutter="20"> </el-icon>
<el-col :span="12"> <h4 :id="titleId" :class="titleClass">{{ title }}</h4>
<el-form-item label="操作员:"> </div>
<el-input disabled v-model="detailData.czy" /> </template>
</el-form-item> <div v-if="equipment" class="drawer-content">
<el-form-item label="辐射范围:"> <!-- 基础信息 -->
<el-input disabled v-model="detailData.fsfw" /> <div class="basic-info-area">
</el-form-item> <DetailInfoBox :data="equipment" :title="'基础信息'" :dataConfig="showDataConfig['基础信息']" />
<el-form-item label="管理人员:">
<el-input disabled v-model="detailData.glry" /> <div class="right-wrapper">
</el-form-item> <div>
<el-form-item label="购买费用(万元):"> <el-tag v-if="equipment.sbzt === '完好'" type="success" size="small">完好</el-tag>
<el-input disabled v-model="detailData.gmfy" /> <el-tag v-if="equipment.sbzt === '损坏'" type="danger" size="small">损坏</el-tag>
</el-form-item> <el-tag v-if="equipment.sbzt === '报废'" type="danger" size="small">报废</el-tag>
<el-form-item label="购置日期:"> </div>
<el-input disabled v-model="detailData.gzrq" /> <div class="photo-block">
</el-form-item> <el-image v-if="defaultPhoto" style="width: 100%; height: 100%" :src="defaultPhoto" :zoom-rate="1.2"
<el-form-item label="区县名称:"> :max-scale="7" :min-scale="0.2" :preview-src-list="photos" show-progress :initial-index="4"
<el-input disabled v-model="detailData.qxmc" /> fit="cover" />
</el-form-item> <div v-else class="no-photo">
<el-form-item label="设备编号:"> <span>暂无图片</span>
<el-input disabled v-model="detailData.sbbh" /> </div>
</el-form-item> </div>
<el-form-item label="设备大类:"> </div>
<el-input disabled v-model="detailData.sbdl" /> </div>
</el-form-item> <!-- 设备位置 -->
<el-form-item label="设备小类:"> <DetailInfoBox class="detail-info-box" :data="equipment" :title="'设备位置'" :dataConfig="showDataConfig['设备位置']" />
<el-input disabled v-model="detailData.sbxl" />
</el-form-item> <!-- 设备管理情况 -->
</el-col> <DetailInfoBox class="detail-info-box" :data="equipment" :title="'设备管理情况'"
<el-col :span="12"> :dataConfig="showDataConfig['设备管理情况']" />
<el-form-item label="设备名称:"> </div>
<el-input disabled v-model="detailData.sbmc" />
</el-form-item> <!-- 设备情况 -->
<el-form-item label="设备位置:"> <template v-for="(item, index) in statusList">
<el-input disabled v-model="detailData.sbwz" /> <DetailInfoBox class="detail-info-box" :data="item" :title="index == 0 ? '设备情况' : ''"
</el-form-item> :dataConfig="showDataConfig['设备情况']" />
<el-form-item label="设备经度:"> </template>
<el-input disabled v-model="detailData.jd" /> </el-drawer>
</el-form-item>
<el-form-item label="设备纬度:">
<el-input disabled v-model="detailData.wd" />
</el-form-item>
<el-form-item label="设备型号:">
<el-input disabled v-model="detailData.sbxh" />
</el-form-item>
<el-form-item label="设备状态:">
<el-input disabled v-model="detailData.sbzt" />
</el-form-item>
<el-form-item label="生产厂家:">
<el-input disabled v-model="detailData.sccj" />
</el-form-item>
<el-form-item label="是否应急设备:">
<el-input disabled v-model="detailData.sfyjsb" />
</el-form-item>
<el-form-item label="是否纳入市级补助范围:">
<el-input disabled v-model="detailData.sfnrsjbz" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div> </div>
</template> </template>
<script setup> <script setup>
const props = defineProps({ import { ref } from 'vue'
detailData: { import { Close } from '@element-plus/icons-vue'
type: Object, import DetailInfoBox from '@/component/DetailInfoBox/DetailInfoBox.vue'
default: () => ({}), import { formatDate } from '../../../../shared/utils'
const visible = ref(false)
const title = ref('设备详情')
const equipment = ref(null)
const statusList = ref([])
const defaultPhoto = ref('')
const photos = ref([])
const showDialog = (data) => {
equipment.value = data.equipment || {}
statusList.value = data.statusList || []
photos.value = data.photos?.length ? data.photos.map((item) => item.photoUrl) : []
defaultPhoto.value = photos.value && photos.value[0] || null
visible.value = true
}
const showDataConfig = ref({
'基础信息':
[{
"label": "设备名称",
"name": "sbmc"
}, {
"label": "设备大类",
"name": "sbdl"
}, {
"label": "设备类型",
"name": "sblx"
}, {
"label": "设备编号",
"name": "sbbh"
}, {
"label": "设备型号",
"name": "sbxh"
}, {
"label": "生产商",
"name": "sccj"
}, {
"label": "所属服务站",
"name": "yhzmc"
}, {
"label": "上传人",
"name": "czy"
}, {
"label": "上传时间",
"name": "tjsj"
}, {
"label": "备注",
"name": "remark"
}],
"设备位置": [{ "label": "设备位置", "name": "sbwz" }, { "label": "设备经度", "name": "jd" }, { "label": "设备纬度", "name": "wd" }],
"设备管理情况": [
{ "label": "设备管理人员", "name": "glry" },
{ "label": "操作员", "name": "czy" },
{
"label": "购置日期", "name": "gzrq",
"value": (item) => {
return item.gzrq ? formatDate(item.gzrq) : ''
}
}, },
}); { "label": "购买费用(万元)", "name": "gmfy" },
{ "label": "应急设备", "name": "sfyjsb" },
{ "label": "纳入市级补助范围", "name": "sfnrsjbz" },
{ "label": "辐射范围", "name": "fsfw" }
],
"设备情况": [
{
"label": (item) => {
if (item.equipmentStatus == '正常') return '设备状态'
if (item.equipmentStatus == '损坏') return '损坏原因'
if (item.equipmentStatus == '报废') return '报废原因'
},
"value": (item) => {
if (item.equipmentStatus == '正常') return '正常'
if (item.equipmentStatus == '损坏') return item.statusDesc
if (item.equipmentStatus == '报废') return item.statusDesc
}
},
{
"label": "上报时间",
"value": (item) => {
return item.reportTime ? formatDate(item.reportTime) : ''
}
},
{
"label": "上报人",
"name": "userName"
}
]
})
defineExpose({
showDialog
})
</script> </script>
<style> <style lang="scss" scoped>
.drawer-header {
display: flex;
align-items: center;
.el-icon--close {
font-size: 20px;
margin-right: 10px;
cursor: pointer;
}
}
.basic-info-area {
position: relative;
.right-wrapper {
position: absolute;
top: 45px;
right: 50px;
display: flex;
gap: 30px;
.photo-block {
width: 150px;
height: 167px;
.no-photo {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
border-radius: 6px;
color: #999;
}
}
}
}
:deep(.detail-info-box) {
.info-item-list-wrapper {
grid-template-columns: repeat(3, 1fr);
}
}
:deep(.el-drawer__header) {
margin-bottom: 20px;
}
:deep(.el-drawer__body) {
padding: 15px;
background-color: rgb(242, 242, 242);
}
</style> </style>

View File

@ -5,6 +5,7 @@ import DetailDialog from "./detailDialog.vue";
import EditDialog from "./editDialog.vue"; import EditDialog from "./editDialog.vue";
import AddDialog from "./addDialog.vue"; import AddDialog from "./addDialog.vue";
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { formatDate } from '../../../../shared/utils'
const treeData = ref([]); const treeData = ref([]);
const treeProps = { const treeProps = {
@ -15,6 +16,7 @@ const treeProps = {
const filterText = ref(''); // 树节点过滤条件 const filterText = ref(''); // 树节点过滤条件
const tableData = ref([]); const tableData = ref([]);
const qxmc = ref(''); // 区县名称 const qxmc = ref(''); // 区县名称
const selectedYHZData = ref(null); // 被选中的养护站数据
const yhzid = ref(''); // 养护站id const yhzid = ref(''); // 养护站id
const filterData = reactive({ const filterData = reactive({
sbmc: '', sbmc: '',
@ -44,6 +46,7 @@ const model = reactive({
}); // 弹窗内容 }); // 弹窗内容
const dialogType = ref(''); // 弹窗类型 const dialogType = ref(''); // 弹窗类型
const dialogRef = ref(null); // 弹窗实例 const dialogRef = ref(null); // 弹窗实例
const detailDialogRef = ref(null); // 详情信息弹窗
const INIT_FORM = { const INIT_FORM = {
rid: "", rid: "",
@ -109,7 +112,12 @@ const getTreeData = async () => {
children: qx.yhzList.map(site => ({ children: qx.yhzList.map(site => ({
id: site.id, id: site.id,
name: `${site.mc}(${site.sbsl})`, name: `${site.mc}(${site.sbsl})`,
type: 'site' type: 'site',
qxmc: qx.qxmc,
rawName: site.mc,
jd: site.jd,
wd: site.wd,
sbwz: qx.qxmc
})), })),
rawName: qx.qxmc, // 原始名称 rawName: qx.qxmc, // 原始名称
}) })
@ -130,18 +138,21 @@ const handleNodeClick = (data, node) => {
console.log('树节点关闭', node.expanded) console.log('树节点关闭', node.expanded)
yhzid.value = ''; // 重置养护站id yhzid.value = ''; // 重置养护站id
qxmc.value = ''; // 重置区县名称 qxmc.value = ''; // 重置区县名称
selectedYHZData.value = null;
return; return;
} }
if (data.type === 'area') { if (data.type === 'area') {
console.log('你点击的是区县', data.id) console.log('你点击的是区县', data.id)
yhzid.value = ''; // 重置养护站id yhzid.value = ''; // 重置养护站id
qxmc.value = data.id; // 保存区县名称 qxmc.value = data.id; // 保存区县名称
selectedYHZData.value = null
} }
if (data.type === 'site') { if (data.type === 'site') {
console.log('你点击的是站点', data.name) console.log('你点击的是站点', data.name)
yhzid.value = data.id; // 保存养护站id yhzid.value = data.id; // 保存养护站id
qxmc.value = ''; // 重置区县名称 qxmc.value = ''; // 重置区县名称
selectedYHZData.value = data
} }
}; };
@ -205,6 +216,11 @@ const columns = [
{ {
prop: 'gzrq', prop: 'gzrq',
label: '购置日期', label: '购置日期',
width: 160,
render: (row) => {
const el = h('span', null, [row.gzrq ? formatDate(row.gzrq) : ''])
return el
}
}, },
{ {
prop: 'gmfy', prop: 'gmfy',
@ -214,6 +230,14 @@ const columns = [
prop: 'qxmc', prop: 'qxmc',
label: '所属区县', label: '所属区县',
}, },
{
label: '上传时间',
width: 160,
render: (row) => {
const el = h('span', null, [row.tjsj ? formatDate(row.tjsj) : ''])
return el
}
},
{ {
label: "操作", label: "操作",
fixed: "right", fixed: "right",
@ -232,18 +256,18 @@ const columns = [
}, },
() => "详情" () => "详情"
), ),
h( // h(
ElButton, // ElButton,
{ // {
type: "primary", // type: "primary",
link: true, // link: true,
onClick: async () => { // onClick: async () => {
dialogType.value = 'edit' // dialogType.value = 'edit'
await getDetailData(row); // await getDetailData(row);
}, // },
}, // },
() => "编辑" // () => "编辑"
), // ),
h( h(
ElButton, ElButton,
{ {
@ -317,20 +341,9 @@ const getDetailData = async (row) => {
} }
if (res.code === '00000') { if (res.code === '00000') {
if (dialogType.value === 'detail') { if (dialogType.value === 'detail') {
model.title = `设备详情`; // 打开详情
model.content = DetailDialog; detailDialogRef.value.showDialog(res.data)
model.props = {
detailData: res.data.equipment,
};
model.onCancel = () => {
dialogType.value = '';
modelVisible.value = false;
};
model.onConfirm = () => {
dialogType.value = '';
modelVisible.value = false;
};
modelVisible.value = true;
} }
if (dialogType.value === 'edit') { if (dialogType.value === 'edit') {
model.title = `编辑设备`; model.title = `编辑设备`;
@ -365,8 +378,16 @@ const getDetailData = async (row) => {
const openAddEquipmentModel = () => { const openAddEquipmentModel = () => {
model.title = `新增设备`; model.title = `新增设备`;
model.content = AddDialog; model.content = AddDialog;
Object.assign(form, INIT_FORM); const extraData = {
jd: selectedYHZData.value.jd,
wd: selectedYHZData.value.wd,
yhzid: selectedYHZData.value.id,
qxmc: selectedYHZData.value.qxmc,
sbwz: selectedYHZData.value.sbwz,
}
Object.assign(form, INIT_FORM, extraData);
model.props = { model.props = {
yhzData: selectedYHZData,
detailData: {}, detailData: {},
form: form, form: form,
}; };
@ -380,7 +401,9 @@ const openAddEquipmentModel = () => {
const res = await request({ const res = await request({
url: '/snow-ops-platform/yjsb/add', url: '/snow-ops-platform/yjsb/add',
method: 'POST', method: 'POST',
data: toRaw(form), data: {
equipment: toRaw(form)
},
}); });
if (res.code === '00000') { if (res.code === '00000') {
ElMessage.success('新增成功'); ElMessage.success('新增成功');
@ -478,5 +501,8 @@ export default () => {
dialogRef, dialogRef,
model, model,
openAddEquipmentModel, openAddEquipmentModel,
selectedYHZData,
detailDialogRef
} }
} }

View File

@ -50,6 +50,7 @@
<div class="form-box"> <div class="form-box">
<div class="event-box"> <div class="event-box">
<el-button <el-button
v-if="selectedYHZData"
type="primary" type="primary"
size="large" size="large"
@click="script.openAddEquipmentModel" @click="script.openAddEquipmentModel"
@ -78,20 +79,23 @@
:onConfirm="script.model?.onConfirm" :onConfirm="script.model?.onConfirm"
:onCancel="script.model?.onCancel" :onCancel="script.model?.onCancel"
ref="dialogRef" ref="dialogRef"
width="60%" width="70%"
> >
</MyDialog> </MyDialog>
</div> </div>
</div> </div>
<detailDialog ref="detailDialogRef" />
</template> </template>
<script setup> <script setup>
import scriptFn from "./index.js"; import scriptFn from "./index.js";
import DynamicTable from "../../component/DynamicTable"; import DynamicTable from "../../component/DynamicTable";
import MyDialog from "../../component/MyDialog"; import MyDialog from "../../component/MyDialog";
import detailDialog from "./detailDialog.vue";
const script = scriptFn(); const script = scriptFn();
const { treeRef, dialogRef } = script; const { treeRef, dialogRef, detailDialogRef, selectedYHZData } = script;
</script> </script>
<style scoped> <style scoped>
.root { .root {

View File

@ -3,7 +3,7 @@
<el-card shadow="hover"> <el-card shadow="hover">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
<el-descriptions title="基础信息" column="1"> <el-descriptions title="基础信息" :column="1">
<el-descriptions-item label="物资名称: ">{{ <el-descriptions-item label="物资名称: ">{{
detailData.material?.wzmc detailData.material?.wzmc
}}</el-descriptions-item> }}</el-descriptions-item>
@ -38,7 +38,7 @@
</el-row> </el-row>
</el-card> </el-card>
<el-card shadow="hover"> <el-card shadow="hover">
<el-descriptions title="物资位置信息" column="3"> <el-descriptions title="物资位置信息" :column="3">
<el-descriptions-item label="区县名称: ">{{ <el-descriptions-item label="区县名称: ">{{
detailData.material?.qxmc detailData.material?.qxmc
}}</el-descriptions-item> }}</el-descriptions-item>
@ -51,7 +51,7 @@
</el-descriptions> </el-descriptions>
</el-card> </el-card>
<el-card shadow="hover"> <el-card shadow="hover">
<el-descriptions title="物资管理情况" column="3"> <el-descriptions title="物资管理情况" :column="3">
<el-descriptions-item label="负责人: ">{{ <el-descriptions-item label="负责人: ">{{
detailData.material?.fzr detailData.material?.fzr
}}</el-descriptions-item> }}</el-descriptions-item>

View File

@ -0,0 +1,52 @@
<template>
<div class="detail-container">
<el-card shadow="hover">
<el-descriptions title="基础信息" :column="3">
<el-descriptions-item label="服务站名称">{{
basicData.mc
}}</el-descriptions-item>
<el-descriptions-item label="服务站经度">{{
basicData.jd
}}</el-descriptions-item>
<el-descriptions-item label="服务站纬度">{{
basicData.wd
}}</el-descriptions-item>
<el-descriptions-item label="设备数">{{}}</el-descriptions-item>
<el-descriptions-item label="物资数">{{}}</el-descriptions-item>
<el-descriptions-item label="人员数">{{}}</el-descriptions-item>
<el-descriptions-item label="创建日期">{{}}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card shadow="hover">
<el-descriptions title="配置信息" :column="3"> </el-descriptions>
<el-tabs v-model="activeTab" class="demo-tabs" @tab-click="handleClick">
<el-tab-pane label="人员信息" name="people">人员信息</el-tab-pane>
<el-tab-pane label="物资信息" name="second">物资信息</el-tab-pane>
<el-tab-pane label="设备信息" name="third">设备信息</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
<script setup>
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
const props = defineProps({
basicData: {
type: Object,
default: () => ({}),
},
});
const activeTab = ref("people");
const handleClick = (tab, event) => {
console.log("tab", tab, "event", event);
};
</script>
<style>
.detail-container {
display: flex;
flex-direction: column;
gap: 30px;
}
</style>

View File

@ -0,0 +1,266 @@
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
import { request } from "@/utils/request";
import { useRoute, useRouter } from 'vue-router'
import DetailDrawer from "./detailDrawer.vue";
const tableData = ref([]); // 表格数据
const filterData = reactive({
zdlx: "",
mc: "",
}) // 过滤条件
const modelVisible = ref(false); // 弹窗状态
const drawerVisible = ref(false); // 抽屉状态
const model = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
}); // 弹窗内容
const drawer = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
direction: 'rtl',
size: '50%'
}); // 抽屉内容
const dialogType = ref(''); // 弹窗类型
const dialogRef = ref(null); // 弹窗实例
const drawerType = ref(''); // 抽屉类型
const drawerRef = ref(null); // 抽屉实例
// 站点类型选项
const zdlxOptions = [
{ label: "服务设施", value: "服务设施" },
{ label: "加油站", value: "加油站" },
{ label: "加气站", value: "加气站" },
{ label: "加氢站", value: "加氢站" },
{ label: "充电站", value: "充电站" },
];
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
pageSizes: [10, 20, 50],
layout: "prev, pager, next, jumper",
onChange: (page, pageSize) => {
pagination.current = page;
pagination.pageSize = pageSize;
getTableData(filterData);
},
}); // 分页
// 获取养护站列表
const getTableData = async (filterData) => {
try {
const res = await request({
url: "/snow-ops-platform/yhz/list",
method: "GET",
params: {
mc: filterData?.mc || "",
zdlx: filterData?.zdlx || "",
pageNum: pagination.current,
pageSize: pagination.pageSize,
},
});
if (res.code === "00000") {
tableData.value = res.data.records;
pagination.total = Number(res.data.total);
} else {
throw new Error(res.message);
}
} catch (error) {
console.log(error.message);
}
};
// 获取养护站详情
const getYhzDetail = async (row) => {
try {
console.log('row',toRaw(row));
const res = await request({
url: `/snow-ops-platform/yhz/getById?id=${row.id}`,
method: "GET",
});
if (res.code === "00000") {
if (drawerType.value === "detail"){
drawer.title = '详情'
drawer.props = {
basicData: res.data,
};
drawer.onCancel = () => {
drawerType.value = '';
drawerVisible.value = false;
};
drawer.onConfirm = () => {
drawerType.value = '';
drawerVisible.value = false;
};
drawer.content = DetailDrawer;
drawerVisible.value = true;
}
}
} catch (error) {
}
};
export default () => {
const router = useRouter();
const handleClickSb = (row) => {
router.push({
path: `/yhzsb/${encodeURIComponent(JSON.stringify(row))}`,
});
};
const handleClickWz = (row) => {
router.push({
path: `/yhzwz/${encodeURIComponent(JSON.stringify(row))}`,
});
};
const columns = [
{
prop: "mc",
label: "服务站名称",
},
{
prop: "sbsl",
label: "设备数量",
render: (row) => () =>
h(
ElButton,
{
link: true,
type: "primary",
onClick: () => handleClickSb(row),
},
() => row.sbsl
),
},
{
prop: "wzsl",
label: "物资数量",
render: (row) => () =>
h(
ElButton,
{
link: true,
type: "primary",
onClick: () => handleClickWz(row),
},
() => row.wzsl
),
},
{
prop: "rysl",
label: "人员数量",
},
{
prop: "qxmc",
label: "区县名称",
},
{
label: "操作",
fixed: "right",
width: 150,
render: (row) => () =>
h("div", { class: "action-btns" }, [
h(
ElButton,
{
type: "primary",
link: true,
onClick: async () => {
drawerType.value = "detail";
await getYhzDetail(row);
},
},
() => "详情"
),
h(
ElButton,
{
type: "success",
link: true,
style: "margin-left: 10px;",
onClick: async () => {
},
},
() => "编辑"
),
h(
ElButton,
{
type: "danger",
link: true,
style: "margin-left: 10px;",
onClick: async () => {
try {
await ElMessageBox.confirm("确定要删除该站点吗?", "删除确认", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
const res = await request({
url: `/snow-ops-platform/yhz/delete`,
method: "POST",
data: {
id: row.id,
},
});
if (res.code === "00000") {
ElMessage.success("删除成功");
getTableData();
}
} catch (error) {
if (error instanceof Error) {
ElMessage.error(error.message || "删除失败");
}
}
},
},
() => "删除"
),
]),
},
];
// 监听过滤条件变化
watch(
[() => filterData],
([newFilterData]) => {
getTableData(newFilterData)
},
{ deep: true }
)
onMounted(async () => {
await getTableData()
});
return {
tableData,
filterData,
zdlxOptions,
pagination,
columns,
handleClickSb,
handleClickWz,
modelVisible,
model,
drawerVisible,
drawer,
dialogRef,
drawerRef,
}
}

View File

@ -2,21 +2,19 @@
<div class="root"> <div class="root">
<div class="search-box"> <div class="search-box">
<el-input <el-input
v-model="params.mc" v-model="script.filterData.mc"
style="width: 240px; margin-right: 10px" style="width: 240px; margin-right: 10px"
size="large" size="large"
placeholder="请输入服务点名称" placeholder="请输入服务点名称"
:suffix-icon="Search" :suffix-icon="Search"
@change="handleSearch"
/> />
<el-select <el-select
v-model="params.zdlx" v-model="script.filterData.zdlx"
style="width: 240px; margin-right: 10px" style="width: 240px; margin-right: 10px"
size="large" size="large"
placeholder="站点类型" placeholder="站点类型"
:suffix-icon="Search" :suffix-icon="Search"
@change="handleSearch" :options="script.zdlxOptions"
:options="zdlxOptions"
clearable clearable
/> />
</div> </div>
@ -26,547 +24,48 @@
> >
</div> </div>
<DynamicTable <DynamicTable
:dataSource="tableData" :dataSource="script.tableData.value"
:columns="columns" :columns="script.columns"
:autoHeight="true" :autoHeight="true"
:pagination="pagination" :pagination="script.pagination"
></DynamicTable> ></DynamicTable>
<div class="dialog-container"> <div class="model-box">
<MyDialog <MyDialog
v-model="dialogVisible" v-model="script.modelVisible.value"
title="站点详情" :title="script.model?.title"
:onConfirm="handleConfirm" :dynamicComponent="script.model?.content"
:onCancel="handleCancel" :component-props="script.model?.props"
width="60%" :onConfirm="script.model?.onConfirm"
:onCancel="script.model?.onCancel"
ref="dialogRef"
:width="script.model?.width"
> >
<el-tabs
v-model="activeName"
class="demo-tabs"
@tab-click="handleClick"
>
<el-tab-pane label="基本信息" name="Basic">
<el-form
label-position="right"
label-width="auto"
style="overflow-y: auto; max-height: 60vh; padding-right: 50px"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="区县名称:" prop="qxmc">
<el-input disabled v-model="detailData.qxmc" />
</el-form-item>
<el-form-item label="站点类型:" prop="zdlx">
<el-select
disabled
v-model="detailData.zdlx"
placeholder="请选择站点类型"
:options="zdlxOptions"
/>
</el-form-item>
<el-form-item label="路线编码:" prop="lxbm">
<el-input disabled v-model="detailData.lxbm" />
</el-form-item>
<el-form-item label="里程桩号:" prop="lczh">
<el-input disabled v-model="detailData.lczh" />
</el-form-item>
<el-form-item label="行政等级:" prop="xzdj">
<el-input disabled v-model="detailData.xzdj" />
</el-form-item>
<el-form-item label="机动车月交通流量:" prop="yjtll">
<el-input disabled v-model="detailData.yjtll" />
</el-form-item>
<el-form-item label="技术等级:" prop="jsdj">
<el-input disabled v-model="detailData.jsdj" />
</el-form-item>
<el-form-item label="备注:" prop="bz">
<el-input
disabled
v-model="detailData.bz"
type="textarea"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="服务保障点具体名称:" prop="mc">
<el-input disabled v-model="detailData.mc" />
</el-form-item>
<el-form-item label="产权隶属单位:" prop="ssdw">
<el-input disabled v-model="detailData.ssdw" />
</el-form-item>
<el-form-item label="经度:" prop="jd">
<el-input disabled v-model="detailData.jd" />
</el-form-item>
<el-form-item label="纬度:" prop="wd">
<el-input disabled v-model="detailData.wd" />
</el-form-item>
<el-form-item label="充电桩根数:" prop="cdzgs">
<el-input disabled v-model="detailData.cdzgs" />
</el-form-item>
<el-form-item label="是否对外开放:" prop="sfdwkf">
<el-input disabled v-model="detailData.sfdwkf" />
</el-form-item>
<el-form-item
label="对外开放的是否有如厕功能:"
prop="sfdwkfrc"
>
<el-input disabled v-model="detailData.sfdwkfrc" />
</el-form-item>
</el-col>
</el-row> </el-form
></el-tab-pane>
<el-tab-pane label="人员信息" name="Person">
<PersonData
:yhzData="detailData"
>
</PersonData>
</el-tab-pane>
</el-tabs>
</MyDialog>
<MyDialog
v-model="editDialogVisible"
:title="isEdit ? '编辑站点' : '新增站点'"
:onConfirm="isEdit ? handleEditConfirm : handleAddConfirm"
:onCancel="handleEditCancel"
width="60%"
>
<el-form
ref="editFormRef"
:model="editForm"
label-position="right"
label-width="auto"
:rules="{}"
style="overflow-y: auto; max-height: 60vh; padding-right: 50px"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="区县名称:" prop="qxmc">
<el-input v-model="editForm.qxmc" />
</el-form-item>
<el-form-item label="站点类型:" prop="zdlx">
<el-select
v-model="editForm.zdlx"
placeholder="请选择站点类型"
:options="zdlxOptions"
/>
</el-form-item>
<el-form-item label="路线编码:" prop="lxbm">
<el-input v-model="editForm.lxbm" />
</el-form-item>
<el-form-item label="里程桩号:" prop="lczh">
<el-input v-model="editForm.lczh" />
</el-form-item>
<el-form-item label="行政等级:" prop="xzdj">
<el-input v-model="editForm.xzdj" />
</el-form-item>
<el-form-item label="机动车月交通流量:" prop="yjtll">
<el-input v-model="editForm.yjtll" />
</el-form-item>
<el-form-item label="技术等级:" prop="jsdj">
<el-input v-model="editForm.jsdj" />
</el-form-item>
<el-form-item label="备注:" prop="bz">
<el-input v-model="editForm.bz" type="textarea" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="服务保障点具体名称:" prop="mc">
<el-input v-model="editForm.mc" />
</el-form-item>
<el-form-item label="产权隶属单位:" prop="ssdw">
<el-input v-model="editForm.ssdw" />
</el-form-item>
<el-form-item label="经度:" prop="jd">
<el-input v-model="editForm.jd" />
</el-form-item>
<el-form-item label="纬度:" prop="wd">
<el-input v-model="editForm.wd" />
</el-form-item>
<el-form-item label="充电桩根数:" prop="cdzgs">
<el-input v-model="editForm.cdzgs" />
</el-form-item>
<el-form-item label="是否对外开放:" prop="sfdwkf">
<el-input v-model="editForm.sfdwkf" />
</el-form-item>
<el-form-item label="对外开放的是否有如厕功能:" prop="sfdwkfrc">
<el-input v-model="editForm.sfdwkfrc" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</MyDialog> </MyDialog>
<MyDrawer
v-model="script.drawerVisible.value"
:title="script.drawer?.title"
:dynamicComponent="script.drawer?.content"
:component-props="script.drawer?.props"
:onConfirm="script.drawer?.onConfirm"
:onCancel="script.drawer?.onCancel"
ref="drawerRef"
:direction="script.drawer?.direction"
:size="script.drawer?.size"
></MyDrawer>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script setup>
import DynamicTable from "../../component/DynamicTable"; import DynamicTable from "../../component/DynamicTable";
import { h, ref, onMounted, reactive, watch, toRaw } from "vue"; import { h, ref, onMounted, reactive, watch, toRaw } from "vue";
import { useRouter, useRoute } from "vue-router";
import { request } from "@/utils/request";
import { Search } from "@element-plus/icons-vue"; import { Search } from "@element-plus/icons-vue";
import MyDialog from "../../component/MyDialog"; import MyDialog from "../../component/MyDialog";
import PersonData from "./personData.vue"; import MyDrawer from "../../component/MyDrawer";
import scriptFn from "./index.js";
const tableData = ref([]); const script = scriptFn();
const detailData = ref({}); const { dialogRef, drawerRef } = script;
const dialogVisible = ref(false);
const handleCancel = () => {
dialogVisible.value = false;
};
const handleConfirm = () => {
dialogVisible.value = false;
};
//
const INIT_FORM = {
id: "",
zdlx: "",
lxbm: "",
qxmc: "",
lczh: "",
xzdj: "",
yjtll: 0,
jsdj: "",
mc: "",
ssdw: "",
jd: "",
wd: "",
cdzgs: "",
sfdwkf: "",
sfdwkfrc: "",
bz: "",
};
//
const zdlxOptions = [
{ label: "服务设施", value: "服务设施" },
{ label: "加油站", value: "加油站" },
{ label: "加气站", value: "加气站" },
{ label: "加氢站", value: "加氢站" },
{ label: "充电站", value: "充电站" },
];
//
const isEdit = ref(false);
const editFormRef = ref();
const editForm = reactive({ ...INIT_FORM });
const editDialogVisible = ref(false);
const openEditDialog = (row) => {
editForm.id = row.id;
Object.assign(editForm, row);
editDialogVisible.value = true;
};
//
const editRules = {
qxmc: [{ required: true, message: "请输入区县名称", trigger: "blur" }],
zdlx: [{ required: true, message: "请输入站点类型", trigger: "blur" }],
lxbm: [{ required: true, message: "请输入路线编码", trigger: "blur" }],
lczh: [{ required: true, message: "请输入里程桩号", trigger: "blur" }],
xzdj: [{ required: true, message: "请输入行政等级", trigger: "blur" }],
yjtll: [
{
required: true,
message: "请输入机动车月交通流量",
trigger: "blur",
},
{
type: "number",
message: "请输入正确的机动车月交通流量",
trigger: "blur",
},
],
jsdj: [{ required: true, message: "请输入技术等级", trigger: "blur" }],
mc: [
{ required: true, message: "请输入服务保障点具体名称", trigger: "blur" },
],
ssdw: [{ required: true, message: "请输入产权隶属单位", trigger: "blur" }],
jd: [{ required: true, message: "请输入经度", trigger: "blur" }],
wd: [{ required: true, message: "请输入纬度", trigger: "blur" }],
cdzgs: [{ required: true, message: "请输入充电桩根数", trigger: "blur" }],
sfdwkf: [{ required: true, message: "请输入是否对外开放", trigger: "blur" }],
sfdwkfrc: [
{
required: true,
message: "请输入对外开放的是否有如厕功能",
trigger: "blur",
},
],
};
const handleEditCancel = () => {
editDialogVisible.value = false;
};
const handleEditConfirm = async () => {
try {
await editFormRef.value.validate();
const res = await request({
url: `/api/yhz/update`,
method: "POST",
data: toRaw(editForm),
});
if (res.code === "00000") {
ElMessage({
message: "编辑成功",
type: "success",
});
getTableData();
editDialogVisible.value = false;
} else {
throw new Error(res.message);
}
} catch (error) {
console.log(error.message);
ElMessage({
message: error.message,
type: "error",
});
}
};
const resetForm = () => {
Object.assign(editForm, INIT_FORM);
};
//
const openAddDialog = () => {
resetForm();
isEdit.value = false;
editDialogVisible.value = true;
};
//
const handleAddConfirm = async () => {
try {
await editFormRef.value.validate();
const res = await request({
url: "/api/yhz/add",
method: "POST",
data: toRaw(editForm),
});
if (res.code === "00000") {
ElMessage({
message: "新增成功",
type: "success",
});
getTableData();
editDialogVisible.value = false;
} else {
throw new Error(res.message);
}
} catch (error) {
ElMessage({
message: error.message,
type: "error",
});
console.log(error.message);
}
};
//
const params = reactive({
mc: "",
zdlx: "",
});
const handleSearch = () => {
//
pagination.current = 1;
getTableData();
};
//
const getDetailData = async (row) => {
try {
const res = await request({
url: `/snow-ops-platform/yhz/getById?id=${row.id}`,
method: "GET",
});
if (res.code === "00000") {
detailData.value = res.data;
return res.code;
} else {
detailData.value = {};
throw new Error(res.message);
}
} catch (error) {
ElMessage({
message: error.message,
type: "error",
});
console.log(error.message);
return -1;
}
};
//
const getTableData = async () => {
try {
const res = await request({
url: "/snow-ops-platform/yhz/list",
method: "GET",
params: {
...params,
pageNum: pagination.current,
pageSize: pagination.pageSize,
},
});
if (res.code === "00000") {
tableData.value = res.data.records;
pagination.total = Number(res.data.total);
} else {
throw new Error(res.message);
}
} catch (error) {
console.log(error.message);
}
};
onMounted(() => {
getTableData();
});
const columns = [
{
prop: "mc",
label: "服务站名称",
},
{
prop: "sbsl",
label: "设备数量",
render: (row) => () =>
h(
ElButton,
{
link: true,
type: "primary",
onClick: () => handleClickSb(row),
},
() => row.sbsl
),
},
{
prop: "wzsl",
label: "物资数量",
render: (row) => () =>
h(
ElButton,
{
link: true,
type: "primary",
onClick: () => handleClickWz(row),
},
() => row.wzsl
),
},
{
prop: "rysl",
label: "人员数量",
},
{
prop: "qxmc",
label: "区县名称",
},
{
label: "操作",
fixed: "right",
width: 150,
render: (row) => () =>
h("div", { class: "action-btns" }, [
h(
ElButton,
{
type: "primary",
link: true,
onClick: async () => {
const res = await getDetailData(row);
if (res === "00000") {
dialogVisible.value = true;
}
},
},
() => "详情"
),
h(
ElButton,
{
type: "success",
link: true,
style: "margin-left: 10px;",
onClick: async () => {
const res = await getDetailData(row);
if (res === "00000") {
isEdit.value = true;
openEditDialog(row);
}
},
},
() => "编辑"
),
h(
ElButton,
{
type: "danger",
link: true,
style: "margin-left: 10px;",
onClick: async () => {
try {
await ElMessageBox.confirm("确定要删除该站点吗?", "删除确认", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
});
const res = await request({
url: `/snow-ops-platform/yhz/delete`,
method: "POST",
data: {
id: row.id,
},
});
if (res.code === "00000") {
ElMessage.success("删除成功");
getTableData();
}
} catch (error) {
if (error instanceof Error) {
ElMessage.error(error.message || "删除失败");
}
}
},
},
() => "删除"
),
]),
},
];
const pagination = reactive({
current: 1,
pageSize: 10,
total: 0,
pageSizes: [10, 20, 50],
layout: "prev, pager, next, jumper",
onChange: (page: number, pageSize: number) => {
pagination.current = page;
pagination.pageSize = pageSize;
getTableData();
},
});
const router = useRouter();
const route = useRoute();
const handleClickSb = (row) => {
router.push({
path: `/yhzsb/${encodeURIComponent(JSON.stringify(row))}`,
});
};
const handleClickWz = (row) => {
router.push({
path: `/yhzwz/${encodeURIComponent(JSON.stringify(row))}`,
});
};
//
const activeName = ref("Basic");
</script> </script>
<style scoped> <style scoped>
.root { .root {
height: 100%; height: 100%;
@ -575,5 +74,8 @@ const activeName = ref("Basic");
.event-box { .event-box {
margin: 20px 0; margin: 20px 0;
} }
.model-box {
position: absolute;
}
</style> </style>

View File

@ -1,52 +1,128 @@
<template> <template>
<div class="detail-container"> <div class="detail-container">
<el-form <el-card shadow="hover">
label-position="right" <el-descriptions title="基础信息" :column="3">
label-width="150px" <el-descriptions-item label="发生时间: ">{{
style="max-height: 60vh; overflow-y: auto; padding-right: 50px" detailData.event?.occurTime
}}</el-descriptions-item>
<el-descriptions-item label="发生地点: ">{{
detailData.event?.occurLocation
}}</el-descriptions-item>
<el-descriptions-item label="线路编号: ">{{
detailData.event?.routeNo
}}</el-descriptions-item>
<el-descriptions-item label="填报时间: ">{{
detailData.event?.reportTime
}}</el-descriptions-item>
<el-descriptions-item label="填报人: ">{{
detailData.event?.reporterName
}}</el-descriptions-item>
<el-descriptions-item label="所属区县: ">{{
detailData.event?.district
}}</el-descriptions-item>
<el-descriptions-item label="所属服务站: ">{{
detailData.event?.stationName
}}</el-descriptions-item>
</el-descriptions>
<el-row :gutter="20" class="custom-row">
<el-col :span="8">
<div class="stake-item">
<span class="label">起点桩号</span>
<span class="value">{{ detailData.event?.startStakeNo }}</span>
</div>
</el-col>
<el-col :span="8">
<div class="middle-column">
<div class="disaster-mileage">
受灾里程: {{ detailData.event?.disasterMileage }}
</div>
<div class="dash-line-container">
<span class="dash-line"></span>
</div>
</div>
</el-col>
<el-col :span="8">
<div class="stake-item">
<span class="label">止点桩号</span>
<span class="value">{{ detailData.event?.endStakeNo }}</span>
</div>
</el-col>
</el-row>
</el-card>
<el-card shadow="hover">
<el-descriptions title="处置情况" :column="3">
<el-descriptions-item label="处置措施: ">{{
detailData.event?.disposalMeasures
}}</el-descriptions-item>
<el-descriptions-item label="预计恢复时间: ">{{
detailData.event?.expectRecoverTime
}}</el-descriptions-item>
</el-descriptions>
</el-card>
<el-card shadow="hover">
<el-descriptions title="物资情况" :column="3">
<el-descriptions-item label="投入人力: ">{{
detailData.material?.inputManpower + "人次"
}}</el-descriptions-item>
<el-descriptions-item label="投入资金: ">{{
detailData.material?.inputFunds + "万元"
}}</el-descriptions-item>
<el-descriptions-item label="投入设备: ">{{
detailData.material?.inputEquipment + "台班"
}}</el-descriptions-item>
<el-descriptions-item
v-for="(item, index) in detailData.materialUsageList"
:key="index"
:label="item.materialName + ': '"
>{{ item.usageAmount + "" + item.materialUnit }}
</el-descriptions-item>
</el-descriptions>
<el-divider />
<el-descriptions title="通行情况" :column="3">
<el-descriptions-item label="当前通行情况: ">{{
{
1: "正常通行",
2: "限速通行",
3: "封闭交通",
}[detailData.traffic?.currentStatus] || "未知状态"
}}</el-descriptions-item>
<el-descriptions-item label="有无车辆滞留: ">{{
{
0: "无",
1: "有",
}[detailData.traffic?.hasStrandedVehicles] || "未知状态"
}}</el-descriptions-item>
<el-descriptions-item
v-if="detailData.traffic?.hasStrandedVehicles === 1"
label="滞留车辆数: "
>{{
detailData.traffic?.strandedVehicleCount + "辆"
}}</el-descriptions-item
> >
<el-row :gutter="20"> <el-descriptions-item label="实际恢复时间: ">{{
<el-col :span="12"> detailData.traffic?.actualRecoverTime
<el-form-item label="物资名称:"> }}</el-descriptions-item>
<el-input disabled v-model="detailData.wzmc" /> </el-descriptions>
</el-form-item> <el-divider />
<el-form-item label="余量:"> <el-descriptions title="现场情况" :column="3"> </el-descriptions>
<el-input disabled v-model="detailData.ye" /> <el-image
</el-form-item> v-for="(item) in detailData.photos"
<el-form-item label="入库日期:"> :key="item.id"
<el-input disabled v-model="detailData.rkrq" /> style="
</el-form-item> width: 100px;
<el-form-item label="入库单位:"> height: 100px;
<el-input disabled v-model="detailData.rkdw" /> margin: 10px;
</el-form-item> "
<el-row :gutter="20"> :src="item.photoUrl"
<el-col :span="12"> :zoom-rate="1.2"
<el-form-item label="数量:"> :max-scale="7"
<el-input disabled v-model="detailData.sl"/> :min-scale="0.2"
</el-form-item> :preview-src-list="[item.photoUrl]"
</el-col> show-progresss
<el-col :span="12"> fit="cover"
<el-form-item label="单位:"> />
<el-input disabled v-model="detailData.dw" /> </el-form-item </el-card>
></el-col>
</el-row>
</el-col>
<el-col :span="12">
<el-form-item label="存放地点:">
<el-input disabled v-model="detailData.cfdd" />
</el-form-item>
<el-form-item label="负责人:">
<el-input disabled v-model="detailData.fzr" />
</el-form-item>
<el-form-item label="联系电话:">
<el-input disabled v-model="detailData.lxdh" />
</el-form-item>
<el-form-item label="区县名称:">
<el-input disabled v-model="detailData.qxmc" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</div> </div>
</template> </template>
@ -60,4 +136,56 @@ const props = defineProps({
</script> </script>
<style> <style>
.detail-container {
display: flex;
flex-direction: column;
gap: 30px;
}
.stake-item {
display: flex;
align-items: flex-end;
justify-content: center;
gap: 8px;
height: 100%;
padding-bottom: 8px;
}
.label {
color: #606266;
white-space: nowrap;
}
.value {
color: #909399;
}
.custom-row {
margin-top: 30px;
text-align: center;
}
.middle-column {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
}
.disaster-mileage {
margin-bottom: 2px;
color: #606266;
}
.dash-line-container {
width: 100%;
position: relative;
margin-top: 4px;
}
.dash-line {
display: block;
width: 100%;
height: 1px;
background: repeating-linear-gradient(
to right,
#909399 0px,
#909399 2px,
transparent 2px,
transparent 4px
);
}
</style> </style>

View File

@ -19,7 +19,7 @@ const yhzid = ref(''); // 养护站id
const filterData = reactive({ const filterData = reactive({
routeNo: '', // 线路编号 routeNo: '', // 线路编号
stakeNo: '', // 桩号 stakeNo: '', // 桩号
reportTime: '', // 填报时间 reportTime: null, // 填报时间
}); // 表格过滤条件 }); // 表格过滤条件
const pagination = reactive({ const pagination = reactive({
current: 1, current: 1,
@ -44,6 +44,19 @@ const model = reactive({
const dialogType = ref(''); // 弹窗类型 const dialogType = ref(''); // 弹窗类型
const dialogRef = ref(null); // 弹窗实例 const dialogRef = ref(null); // 弹窗实例
const drawerVisible = ref(false); // 抽屉显示状态
const drawer = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
direction: 'rtl',
size: '50%'
}); // 抽屉内容
const drawerType = ref(''); // 抽屉类型
const drawerRef = ref(null); // 抽屉实例
const INIT_FORM = { const INIT_FORM = {
rkrq: "", rkrq: "",
rkdw: "", rkdw: "",
@ -86,7 +99,7 @@ const filterNode = (value, node) => {
const getTreeData = async () => { const getTreeData = async () => {
try { try {
const res = await request({ const res = await request({
url: '/snow-ops-platform/yhz/listAreaGroup', url: '/snow-ops-platform/yhz/listAreaGroup?dataType=ICE_SNOW_EVENT',
method: 'GET' method: 'GET'
}) })
if (res.code === '00000') { if (res.code === '00000') {
@ -134,6 +147,15 @@ const handleNodeClick = (data, node) => {
} }
}; };
// 处理日期格式
const formatDate = (date) => {
if (!date) return '';
const pad = n => n.toString().padStart(2, '0');
const d = new Date(date);
return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ` +
`${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
};
// 获取冰雪事件列表 // 获取冰雪事件列表
const getyhzeventList = async (qxmc, yhzid, filterData) => { const getyhzeventList = async (qxmc, yhzid, filterData) => {
try { try {
@ -142,8 +164,8 @@ const getyhzeventList = async (qxmc, yhzid, filterData) => {
yhzid: yhzid, yhzid: yhzid,
routeNo: filterData?.routeNo || '', routeNo: filterData?.routeNo || '',
stakeNo: filterData?.stakeNo || '', stakeNo: filterData?.stakeNo || '',
reportTimeStart: filterData?.reportTime[0] || '', reportTimeStart: filterData?.reportTime?.[0] ? formatDate(filterData.reportTime[0]) : '',
reportTimeEnd: filterData?.reportTime[1] || '', reportTimeEnd: filterData?.reportTime?.[1] ? formatDate(filterData.reportTime[1]) : '',
pageNum: pagination.current, pageNum: pagination.current,
pageSize: pagination.pageSize, pageSize: pagination.pageSize,
} }
@ -223,24 +245,24 @@ const columns = [
type: "primary", type: "primary",
link: true, link: true,
onClick: async () => { onClick: async () => {
dialogType.value = 'detail' drawerType.value = 'detail'
await getDetailData(row); await getDetailData(row);
}, },
}, },
() => "详情" () => "详情"
), ),
h( // h(
ElButton, // ElButton,
{ // {
type: "primary", // type: "primary",
link: true, // link: true,
onClick: async () => { // onClick: async () => {
dialogType.value = 'edit' // dialogType.value = 'edit'
await getDetailData(row); // await getDetailData(row);
}, // },
}, // },
() => "编辑" // () => "编辑"
), // ),
h( h(
ElButton, ElButton,
{ {
@ -248,16 +270,16 @@ const columns = [
link: true, link: true,
onClick: async () => { onClick: async () => {
try { try {
await ElMessageBox.confirm("确定要删除该设备吗?", "删除确认", { await ElMessageBox.confirm("确定要删除该冰雪事件吗?", "删除确认", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning", type: "warning",
}); });
const res = await request({ const res = await request({
url: `/snow-ops-platform/yjwz/delete`, url: `/snow-ops-platform/event/delete`,
method: 'POST', method: 'POST',
data: { data: {
rid: row.rid, id: row.id,
} }
}); });
if (res.code === '00000') { if (res.code === '00000') {
@ -313,21 +335,21 @@ const getDetailData = async (row) => {
throw new Error('获取事件详情失败') throw new Error('获取事件详情失败')
} }
if (res.code === '00000') { if (res.code === '00000') {
if (dialogType.value === 'detail') { if (drawerType.value === 'detail') {
model.title = `事件详情`; drawer.title = `事件详情`;
model.content = DetailDialog; drawer.content = DetailDialog;
model.props = { drawer.props = {
detailData: res.data, detailData: res.data,
}; };
model.onCancel = () => { drawer.onCancel = () => {
dialogType.value = ''; drawerType.value = '';
modelVisible.value = false; drawerVisible.value = false;
}; };
model.onConfirm = () => { drawer.onConfirm = () => {
dialogType.value = ''; drawerType.value = '';
modelVisible.value = false; drawerVisible.value = false;
}; };
modelVisible.value = true; drawerVisible.value = true;
} }
if (dialogType.value === 'edit') { if (dialogType.value === 'edit') {
model.title = `编辑物资`; model.title = `编辑物资`;
@ -433,5 +455,9 @@ export default () => {
modelVisible, modelVisible,
dialogRef, dialogRef,
model, model,
drawerVisible,
drawer,
drawerRef,
yhzid,
} }
} }

View File

@ -31,7 +31,13 @@
</div> </div>
<div class="inline-flex"> <div class="inline-flex">
<label>填报时间:</label> <label>填报时间:</label>
<el-input v-model="script.filterData.reportTime"></el-input> <el-date-picker
v-model="script.filterData.reportTime"
type="datetimerange"
range-separator="到"
start-placeholder="起始时间"
end-placeholder="结束时间"
/>
</div> </div>
</div> </div>
<div class="form-box"> <div class="form-box">
@ -65,6 +71,17 @@
width="60%" width="60%"
> >
</MyDialog> </MyDialog>
<MyDrawer
v-model="script.drawerVisible.value"
:title="script.drawer?.title"
:dynamicComponent="script.drawer?.content"
:component-props="script.drawer?.props"
:onConfirm="script.drawer?.onConfirm"
:onCancel="script.drawer?.onCancel"
ref="drawerRef"
:direction="script.drawer?.direction"
:size="script.drawer?.size"
></MyDrawer>
</div> </div>
</div> </div>
</template> </template>
@ -73,6 +90,7 @@
import scriptFn from "./index.js"; import scriptFn from "./index.js";
import DynamicTable from "../../component/DynamicTable"; import DynamicTable from "../../component/DynamicTable";
import MyDialog from "../../component/MyDialog"; import MyDialog from "../../component/MyDialog";
import MyDrawer from "../../component/MyDrawer";
const script = scriptFn(); const script = scriptFn();
const { treeRef, dialogRef } = script; const { treeRef, dialogRef } = script;

View File

@ -101,7 +101,6 @@ export default defineConfig(({ command, mode }) => {
outDir: 'dist', outDir: 'dist',
assetsDir: 'assets', assetsDir: 'assets',
sourcemap: false, sourcemap: false,
minify: 'terser',
rollupOptions: { rollupOptions: {
output: { output: {
chunkFileNames: 'js/[name]-[hash].js', chunkFileNames: 'js/[name]-[hash].js',

10
pnpm-lock.yaml generated
View File

@ -113,8 +113,8 @@ importers:
specifier: ^7.2.0 specifier: ^7.2.0
version: 7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3) version: 7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)
vite-plugin-cesium: vite-plugin-cesium:
specifier: ^1.2.23 specifier: 1.2.22
version: 1.2.23(cesium@1.135.0)(rollup@4.53.1)(vite@7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)) version: 1.2.22(cesium@1.135.0)(rollup@4.53.1)(vite@7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3))
vite-plugin-svg-icons: vite-plugin-svg-icons:
specifier: ^2.0.1 specifier: ^2.0.1
version: 2.0.1(vite@7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)) version: 2.0.1(vite@7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3))
@ -2262,8 +2262,8 @@ packages:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
vite-plugin-cesium@1.2.23: vite-plugin-cesium@1.2.22:
resolution: {integrity: sha512-x9A8ZCEoegceXg/E+LnxKr0XBsI9CR4cgYWQ2Dd3cUEYwKcTnHQ3kBfpol7BUcGtgQnQos/mtVrRmuVQBXFjHw==} resolution: {integrity: sha512-OnS+VKNGck4kUu4/67Fdfhz0/zF9mDVNUp9hUWtX19C38O0mJsJy2MH1ev2QcrVLf6VieJ7vCGxkLchdB1n1HQ==}
peerDependencies: peerDependencies:
cesium: ^1.95.0 cesium: ^1.95.0
vite: '>=2.7.1' vite: '>=2.7.1'
@ -4691,7 +4691,7 @@ snapshots:
vary@1.1.2: {} vary@1.1.2: {}
vite-plugin-cesium@1.2.23(cesium@1.135.0)(rollup@4.53.1)(vite@7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)): vite-plugin-cesium@1.2.22(cesium@1.135.0)(rollup@4.53.1)(vite@7.2.2(@types/node@24.10.0)(less@4.4.2)(sass@1.93.3)):
dependencies: dependencies:
cesium: 1.135.0 cesium: 1.135.0
fs-extra: 9.1.0 fs-extra: 9.1.0