PointManage.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <template>
  2. <div class="point-container">
  3. <el-card class="search-card">
  4. <el-form :inline="true" :model="searchForm">
  5. <el-form-item label="测点名称">
  6. <el-input v-model="searchForm.pointName" placeholder="请输入测点名称" clearable />
  7. </el-form-item>
  8. <el-form-item label="测点类型">
  9. <el-select v-model="searchForm.pointType" placeholder="请选择" clearable style="width:160px">
  10. <el-option v-for="d in pointTypeDict" :key="d.dictValue" :label="d.dictLabel" :value="d.dictValue" />
  11. </el-select>
  12. </el-form-item>
  13. <el-form-item>
  14. <el-button type="primary" @click="handleSearch">查询</el-button>
  15. <el-button @click="handleReset">重置</el-button>
  16. <el-button type="success" @click="handleAdd">新增测点</el-button>
  17. </el-form-item>
  18. </el-form>
  19. </el-card>
  20. <el-card class="table-card">
  21. <el-table :data="tableData" border stripe v-loading="loading">
  22. <el-table-column prop="pointCode" label="测点编码" width="120" />
  23. <el-table-column prop="pointName" label="测点名称" min-width="150" />
  24. <el-table-column prop="pointType" label="测点类型" width="110">
  25. <template #default="{ row }">{{ getDictLabel(pointTypeDict, row.pointType) }}</template>
  26. </el-table-column>
  27. <el-table-column label="所属管网" width="150">
  28. <template #default="{ row }">{{ getNetworkName(row.networkId) }}</template>
  29. </el-table-column>
  30. <el-table-column prop="location" label="位置" min-width="180" />
  31. <el-table-column prop="status" label="状态" width="90">
  32. <template #default="{ row }">
  33. <el-tag :type="row.status === 'ACTIVE' ? 'success' : 'info'">{{ getDictLabel(pointStatusDict, row.status) }}</el-tag>
  34. </template>
  35. </el-table-column>
  36. <el-table-column label="操作" width="200" fixed="right">
  37. <template #default="{ row }">
  38. <el-button type="primary" size="small" @click="handleEdit(row)">编辑</el-button>
  39. <el-button type="info" size="small" @click="handleRelDevice(row)">关联设备</el-button>
  40. <el-button type="danger" size="small" @click="handleDelete(row)">删除</el-button>
  41. </template>
  42. </el-table-column>
  43. </el-table>
  44. <el-pagination
  45. v-model:current-page="pageNum" v-model:page-size="pageSize" :total="total"
  46. layout="total, prev, pager, next, sizes" :page-sizes="[10, 20, 50]"
  47. @change="loadData" style="margin-top: 16px; justify-content: flex-end"
  48. />
  49. </el-card>
  50. <el-dialog title="关联监测设备" v-model="deviceDialogVisible" width="950px" destroy-on-close>
  51. <el-form label-width="80px">
  52. <el-form-item label="当前测点">
  53. <span>{{ currentPoint.pointName || '-' }}</span>
  54. </el-form-item>
  55. <el-form-item label="选择设备">
  56. <el-select
  57. v-model="bindForm.equipmentId"
  58. filterable
  59. clearable
  60. placeholder="请选择要关联的设备"
  61. style="width: 100%"
  62. >
  63. <el-option
  64. v-for="item in unboundEquipmentOptions"
  65. :key="item.equipmentId"
  66. :label="`${item.equipmentName}(${item.equipmentCode})`"
  67. :value="item.equipmentId"
  68. />
  69. </el-select>
  70. </el-form-item>
  71. </el-form>
  72. <div style="margin-bottom: 12px">
  73. <el-button type="primary" @click="submitBind">添加关联</el-button>
  74. </div>
  75. <el-table :data="deviceList" border stripe v-loading="deviceLoading">
  76. <el-table-column label="设备编码" prop="equipmentCode" min-width="140" />
  77. <el-table-column label="设备名称" prop="equipmentName" min-width="180" show-overflow-tooltip />
  78. <el-table-column label="设备型号" prop="equipmentModel" min-width="120" />
  79. <el-table-column label="制造商" prop="manufacturer" min-width="140" show-overflow-tooltip />
  80. <el-table-column label="设备位置" prop="equipmentLocation" min-width="180" show-overflow-tooltip />
  81. <el-table-column label="操作" align="center" width="100">
  82. <template #default="{ row: dev }">
  83. <el-button link type="danger" icon="Close" @click="handleUnbind(dev)">解绑</el-button>
  84. </template>
  85. </el-table-column>
  86. </el-table>
  87. </el-dialog>
  88. <el-dialog :title="dialogTitle" v-model="dialogVisible" width="550px" destroy-on-close>
  89. <el-form :model="formData" ref="formRef" label-width="100px">
  90. <el-form-item label="测点编码" prop="pointCode">
  91. <el-input v-model="formData.pointCode" placeholder="请输入测点编码" />
  92. </el-form-item>
  93. <el-form-item label="测点名称" prop="pointName">
  94. <el-input v-model="formData.pointName" placeholder="请输入测点名称" />
  95. </el-form-item>
  96. <el-form-item label="测点类型">
  97. <el-select v-model="formData.pointType" style="width: 100%">
  98. <el-option v-for="d in pointTypeDict" :key="d.dictValue" :label="d.dictLabel" :value="d.dictValue" />
  99. </el-select>
  100. </el-form-item>
  101. <el-form-item label="所属管网">
  102. <el-select v-model="formData.networkId" placeholder="请选择管网" clearable style="width: 100%">
  103. <el-option v-for="n in networkOptions" :key="n.networkId" :label="`${n.networkName}(${n.networkCode})`" :value="n.networkId" />
  104. </el-select>
  105. </el-form-item>
  106. <el-form-item label="位置描述">
  107. <el-input v-model="formData.location" />
  108. </el-form-item>
  109. <el-form-item label="经度">
  110. <el-input-number v-model="formData.longitude" :precision="10" style="width: 100%" />
  111. </el-form-item>
  112. <el-form-item label="纬度">
  113. <el-input-number v-model="formData.latitude" :precision="10" style="width: 100%" />
  114. </el-form-item>
  115. <el-form-item label="状态">
  116. <el-select v-model="formData.status">
  117. <el-option v-for="d in pointStatusDict" :key="d.dictValue" :label="d.dictLabel" :value="d.dictValue" />
  118. </el-select>
  119. </el-form-item>
  120. </el-form>
  121. <template #footer>
  122. <el-button @click="dialogVisible = false">取消</el-button>
  123. <el-button type="primary" @click="handleSave">确定</el-button>
  124. </template>
  125. </el-dialog>
  126. </div>
  127. </template>
  128. <script setup>
  129. import { ref, reactive, computed, onMounted } from 'vue'
  130. import { ElMessage, ElMessageBox } from 'element-plus'
  131. import { getPointPage, addPoint, updatePoint, deletePoint, getEquipmentByPoint, savePointRel, deletePointRel, getEquipmentByTopLevelType, getPipeNetworkOptions } from '@/api/pipeNetwork/basic'
  132. import { getDicts } from '@/api/system/dict/data'
  133. const loading = ref(false)
  134. const tableData = ref([])
  135. const pageNum = ref(1)
  136. const pageSize = ref(10)
  137. const total = ref(0)
  138. const searchForm = reactive({ pointName: '', pointType: '' })
  139. const dialogVisible = ref(false)
  140. const dialogTitle = ref('新增')
  141. const formRef = ref(null)
  142. const formData = reactive({
  143. pointId: '', pointCode: '', pointName: '', pointType: '',
  144. networkId: '', location: '', longitude: null, latitude: null, status: 'ACTIVE'
  145. })
  146. const pointTypeDict = ref([])
  147. const pointStatusDict = ref([])
  148. const networkOptions = ref([])
  149. const deviceDialogVisible = ref(false)
  150. const currentPoint = reactive({ pointId: '', pointName: '' })
  151. const deviceList = ref([])
  152. const deviceLoading = ref(false)
  153. const bindForm = reactive({ equipmentId: '' })
  154. const equipmentOptions = ref([])
  155. const unboundEquipmentOptions = computed(() => {
  156. const boundIds = deviceList.value.map(d => d.equipmentId)
  157. return equipmentOptions.value.filter(e => !boundIds.includes(e.equipmentId))
  158. })
  159. function getDictLabel(dict, value) {
  160. const item = dict.find(d => d.dictValue === String(value))
  161. return item ? item.dictLabel : value
  162. }
  163. function getNetworkName(networkId) {
  164. if (!networkId) return '-'
  165. const n = networkOptions.value.find(o => o.networkId === networkId)
  166. return n ? n.networkName : networkId
  167. }
  168. async function loadDicts() {
  169. const [typeRes, statusRes] = await Promise.all([
  170. getDicts('point_type'),
  171. getDicts('point_status')
  172. ])
  173. pointTypeDict.value = typeRes.data
  174. pointStatusDict.value = statusRes.data
  175. }
  176. async function loadData() {
  177. loading.value = true
  178. try {
  179. const res = await getPointPage(pageNum.value, pageSize.value, searchForm)
  180. tableData.value = res.data.records || []
  181. total.value = res.data.total || 0
  182. } catch (e) { console.error(e) } finally { loading.value = false }
  183. }
  184. function handleSearch() { pageNum.value = 1; loadData() }
  185. function handleReset() { Object.assign(searchForm, { pointName: '', pointType: '' }); handleSearch() }
  186. async function loadNetworkOptions() {
  187. if (networkOptions.value.length > 0) return
  188. try { const res = await getPipeNetworkOptions(); networkOptions.value = res.data || [] } catch (e) { console.error(e) }
  189. }
  190. function handleAdd() {
  191. dialogTitle.value = '新增测点'
  192. Object.assign(formData, { pointId: '', pointCode: '', pointName: '', pointType: '', networkId: '', location: '', longitude: null, latitude: null, status: 'ACTIVE' })
  193. dialogVisible.value = true
  194. loadNetworkOptions()
  195. }
  196. function handleEdit(row) { dialogTitle.value = '编辑'; Object.assign(formData, row); dialogVisible.value = true; loadNetworkOptions() }
  197. async function handleSave() {
  198. try {
  199. if (formData.pointId) { await updatePoint(formData); ElMessage.success('修改成功') }
  200. else { await addPoint(formData); ElMessage.success('新增成功') }
  201. dialogVisible.value = false; loadData()
  202. } catch (e) { console.error(e) }
  203. }
  204. function handleDelete(row) {
  205. ElMessageBox.confirm('确认删除该测点?', '提示', { type: 'warning' }).then(async () => {
  206. await deletePoint(row.pointId); ElMessage.success('删除成功'); loadData()
  207. }).catch(() => {})
  208. }
  209. async function handleRelDevice(row) {
  210. Object.assign(currentPoint, { pointId: row.pointId, pointName: row.pointName })
  211. bindForm.equipmentId = ''
  212. deviceDialogVisible.value = true
  213. if (equipmentOptions.value.length === 0) { await loadEquipmentOptions() }
  214. loadDeviceList()
  215. }
  216. async function loadDeviceList() {
  217. deviceLoading.value = true
  218. try {
  219. const res = await getEquipmentByPoint(currentPoint.pointId)
  220. const raw = res.data || []
  221. const eqMap = new Map(equipmentOptions.value.map(e => [e.equipmentId, e]))
  222. deviceList.value = raw.map(r => {
  223. const full = eqMap.get(r.equipmentId)
  224. return {
  225. relId: r.relId,
  226. equipmentId: r.equipmentId,
  227. equipmentName: full ? full.equipmentName : '',
  228. equipmentCode: full ? full.equipmentCode : '',
  229. equipmentModel: full ? full.equipmentModel : '',
  230. manufacturer: full ? full.manufacturer : '',
  231. equipmentLocation: full ? full.equipmentLocation : ''
  232. }
  233. })
  234. } catch (e) { console.error(e) } finally { deviceLoading.value = false }
  235. }
  236. async function loadEquipmentOptions() {
  237. try {
  238. const res = await getEquipmentByTopLevelType('燃气')
  239. equipmentOptions.value = res.data || []
  240. } catch (e) { console.error(e) }
  241. }
  242. async function submitBind() {
  243. if (!bindForm.equipmentId) { ElMessage.warning('请先选择设备'); return }
  244. try {
  245. await savePointRel({ equipmentId: bindForm.equipmentId, pointId: currentPoint.pointId })
  246. ElMessage.success('关联成功')
  247. bindForm.equipmentId = ''
  248. loadDeviceList()
  249. } catch (e) { console.error(e) }
  250. }
  251. function handleUnbind(dev) {
  252. ElMessageBox.confirm('确认解除该设备关联?', '提示', { type: 'warning' }).then(async () => {
  253. await deletePointRel(dev.relId)
  254. ElMessage.success('解绑成功')
  255. loadDeviceList()
  256. }).catch(() => {})
  257. }
  258. onMounted(async () => { await loadDicts(); loadNetworkOptions(); loadData() })
  259. </script>
  260. <style scoped>
  261. .point-container { padding: 16px; }
  262. .search-card { margin-bottom: 16px; }
  263. </style>