飞天云平台-国产化
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

883 lines
30 KiB

5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
11 hours ago
5 months ago
4 months ago
11 hours ago
5 months ago
4 months ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
5 months ago
5 months ago
11 hours ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
11 hours ago
5 months ago
5 months ago
4 months ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
5 months ago
5 months ago
5 months ago
11 hours ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
11 hours ago
5 months ago
5 months ago
11 hours ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
11 hours ago
5 months ago
4 months ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
4 months ago
11 hours ago
4 months ago
5 months ago
4 months ago
11 hours ago
4 months ago
11 hours ago
5 months ago
5 months ago
5 months ago
9 hours ago
5 months ago
9 hours ago
5 months ago
9 hours ago
5 months ago
9 hours ago
5 months ago
5 months ago
5 months ago
11 hours ago
5 months ago
11 hours ago
5 months ago
  1. <template>
  2. <div class="app-container" style="position: relative; height: 770px; background-color: #fff;">
  3. <div class="position-top" style="display: flex; justify-content: space-between;">
  4. <div class="position-top-left">
  5. <el-select
  6. ref="selectRef"
  7. v-model="selectedValue"
  8. placeholder="请选择"
  9. style="width: 250px;"
  10. @visible-change="handleVisibleChange"
  11. >
  12. <i slot="prefix" class="el-input__icon el-icon-location" />
  13. <el-option value="" style="display: none;" />
  14. <div class="tree-dropdown">
  15. <el-tree
  16. ref="treeRef"
  17. :data="regionTreeData"
  18. :props="treeProps"
  19. node-key="id"
  20. default-expand-all
  21. :expand-on-click-node="false"
  22. @node-click="handleTreeNodeClick"
  23. >
  24. <span slot-scope="{ node, data }" class="custom-tree-node">
  25. <span v-if="data.fondsId" style="font-weight: bold;">
  26. <i class="iconfont icon-gongsi" />
  27. {{ data.label }}
  28. </span>
  29. <span v-else-if="data.floorId">
  30. <i class="iconfont icon-duolouceng" />
  31. {{ data.label }}
  32. </span>
  33. <span v-else>
  34. <i class="iconfont icon-hangzhengquyuguanli" />
  35. {{ data.label }}
  36. </span>
  37. </span>
  38. </el-tree>
  39. </div>
  40. </el-select>
  41. <!-- v-if="bindAllRackList.length !== 0" -->
  42. <el-card class="box-card shelf-bind-card">
  43. <ul class="shelf-bind-tab">
  44. <li :class="{ active: bindStatus === 'bound' }" @click="switchBindStatus('bound')">已绑定{{ boundShelfList.length }}</li>
  45. <li :class="{ active: bindStatus === 'unbound' }" @click="switchBindStatus('unbound')">未绑定{{ unboundShelfList.length }}</li>
  46. </ul>
  47. <ul class="shelf-bind-list">
  48. <li v-for="item in (bindStatus === 'bound' ? boundShelfList : unboundShelfList)" :key="item.id">
  49. <span>{{ item.id }}</span>
  50. <p>{{ item.code }}</p>
  51. </li>
  52. </ul>
  53. </el-card>
  54. </div>
  55. <div v-if="is3DMap" class="position-top-middle">
  56. <el-card class="box-card">
  57. <el-form ref="form" :model="form" label-width="100px">
  58. <el-form-item label="架位筛选" prop="name">
  59. <el-select v-model="form.code1" style="width: 190px;" @change="changeShelfToModel">
  60. <el-option
  61. v-for="(item,index) in options1"
  62. :key="index"
  63. :label="item.label"
  64. :value="item.value"
  65. />
  66. </el-select>
  67. <el-select v-model="form.layerVal" size="small" placeholder="书架排列表" class="filter-item" style="width: 140px;" value-key="id" @change="changeShelfGetGrid">
  68. <el-option v-for="item in layerOptions" :key="item.id" :label="item.name" :value="item" />
  69. </el-select>
  70. <el-select v-model="form.toward" style="width: 80px;" :disabled="towardDisabled">
  71. <el-option
  72. v-for="(item,index) in abOptions"
  73. :key="index"
  74. :label="item.name"
  75. :value="item.value"
  76. />
  77. </el-select>
  78. <el-select v-model="form.shelfVal" size="small" placeholder="架列表" class="filter-item" style="width: 100px;">
  79. <el-option v-for="item in rackOptions" :key="item.id" :label="item.name" :value="item.id" />
  80. </el-select>
  81. <el-button class="filter-item filter-search" size="mini" type="success" icon="el-icon-search" @click="getInitShelfGridByShelfId()" />
  82. </el-form-item>
  83. </el-form>
  84. <div v-if="filterShelfList.length !== 0" class="filter-shelf-list">
  85. <el-tag v-for="(item,index) in filterShelfList" :key="index" type="success">{{ item.gridName }}</el-tag>
  86. </div>
  87. <div class="filter-model-select">
  88. <div class="model-select-style">已选定位模型ID{{ currentFid }}</div>
  89. <div class="model-select-style">已选架位<span>{{ filterShelfList.length }}</span></div>
  90. <el-button class="filter-item filter-search" size="mini" type="success"><i class="iconfont icon-bangding" />绑定</el-button>
  91. </div>
  92. </el-card>
  93. </div>
  94. <div class="position-top-right">
  95. <!-- v-if="bindAllInquiryList.length !== 0" -->
  96. <el-card class="box-card">
  97. <div class="inquiry-machine-bind-top">
  98. <h4>查询机绑定</h4>
  99. <div class="inquiry-machine-bind-params">
  100. <span
  101. :style="{ color: inquiryFilterStatus === 'all' || inquiryFilterStatus === 'bound' ? 'rgb(64, 196, 140)' : 'rgb(153, 153, 153)', fontWeight: inquiryFilterStatus === 'all' || inquiryFilterStatus === 'bound' ? 'bold' : 'normal', cursor: 'pointer' }"
  102. @click="inquiryFilterStatus = inquiryFilterStatus === 'bound' ? 'all' : 'bound'"
  103. >已绑 {{ inquiryList.filter(item => item.bound).length }}</span>
  104. <span>/</span>
  105. <span
  106. :style="{ color: inquiryFilterStatus === 'all' || inquiryFilterStatus === 'unbound' ? 'rgb(237, 74, 65)' : 'rgb(153, 153, 153)', fontWeight: inquiryFilterStatus === 'all' || inquiryFilterStatus === 'unbound' ? 'bold' : 'normal', cursor: 'pointer' }"
  107. @click="inquiryFilterStatus = inquiryFilterStatus === 'unbound' ? 'all' : 'unbound'"
  108. >未绑 {{ inquiryList.filter(item => !item.bound).length }}</span>
  109. </div>
  110. </div>
  111. <ul class="shelf-bind-list" style="height: 190px;">
  112. <li v-for="item in filteredInquiryList" :key="item.id">
  113. <span>{{ item.id }}</span>
  114. <p>{{ item.name }}</p>
  115. <span
  116. :style="{ color: item.bound ? 'rgb(64, 196, 140)' : 'rgb(3, 72, 243)', cursor: 'pointer' }"
  117. @click="item.bound ? handleInquiryLocate(item) : handleInquiryBind(item)"
  118. >{{ item.bound ? '定位' : '绑定' }}</span>
  119. </li>
  120. </ul>
  121. </el-card>
  122. </div>
  123. </div>
  124. <PreviewMap v-if="is3DMap" ref="map" :map-data="mapData" :to-shlef="true" :area-fid="currentFid" @refreshAreaFid="handleAreaFid" />
  125. <el-empty v-else description="当前区域非3D模式的地图" style="height: 100%;" />
  126. <!-- 模型信息面板 -->
  127. <div v-if="currentFid" class="model-info-panel">
  128. <div class="model-info-content">
  129. <div class="model-info-item">
  130. <span class="model-info-label">当前选中</span>
  131. <span class="model-info-value">模型ID{{ currentFid }}</span>
  132. </div>
  133. <div v-if="bindData.rackCount > 0" class="model-info-item">
  134. <span class="model-info-label">绑定架位</span>
  135. <span class="model-info-value">{{ bindData.rackCount }} / </span>
  136. <el-button type="text" class="model-info-link" @click="showDetail('rack')">查看详情</el-button>
  137. </div>
  138. <div v-if="bindData.inquiryCount > 0" class="model-info-item">
  139. <span class="model-info-label">绑定查询机</span>
  140. <span class="model-info-value">{{ bindData.inquiryCount }} / </span>
  141. <el-button type="text" class="model-info-link" @click="showDetail('inquiry')">查看详情</el-button>
  142. </div>
  143. <div v-if="bindData.areaCount > 0" class="model-info-item">
  144. <span class="model-info-label">绑定区域</span>
  145. <span class="model-info-value">{{ bindData.areaCount }} / </span>
  146. <el-button type="text" class="model-info-link" @click="showDetail('area')">查看详情</el-button>
  147. </div>
  148. </div>
  149. </div>
  150. <!-- 详情对话框 -->
  151. <el-dialog
  152. :visible.sync="dialogVisible"
  153. :title="dialogTitle"
  154. width="500px"
  155. append-to-body
  156. :close-on-click-modal="false"
  157. :modal-append-to-body="false"
  158. >
  159. <el-table :data="dialogType === 'rack' ? rackDetails : dialogType === 'inquiry' ? inquiryDetails : areaDetails" style="width: 100%" height="300px" max-height="300px">
  160. <!-- 绑定架位详情表格 -->
  161. <template v-if="dialogType === 'rack'">
  162. <el-table-column prop="id" label="序号" width="80" />
  163. <el-table-column prop="rack" label="架位" />
  164. <el-table-column prop="name" label="名称" />
  165. </template>
  166. <!-- 绑定查询机详情表格 -->
  167. <template v-else-if="dialogType === 'inquiry'">
  168. <el-table-column prop="id" label="序号" width="80" />
  169. <el-table-column prop="name" label="查询机名称" />
  170. <el-table-column prop="ip" label="设备IP" />
  171. </template>
  172. <!-- 绑定区域详情表格 -->
  173. <template v-else-if="dialogType === 'area'">
  174. <el-table-column prop="id" label="序号" width="80" />
  175. <el-table-column prop="code" label="区域编码" />
  176. <el-table-column prop="name" label="区域名称" />
  177. </template>
  178. </el-table>
  179. </el-dialog>
  180. </div>
  181. </template>
  182. <script>
  183. import { FetchRegionTree } from '@/api/deviceVI/index'
  184. import { FetchMapDetails } from '@/api/map/index'
  185. import { FetchInitBookShelfList, FetchBookShelfDetails, FetchInitShelfGridByShelfId } from '@/api/shelf/index'
  186. import PreviewMap from '../map3d/map'
  187. export default {
  188. name: 'Shelf3dPosition',
  189. components: {
  190. PreviewMap
  191. },
  192. data() {
  193. return {
  194. selectedValue: '',
  195. regionTreeData: [],
  196. treeProps: { label: 'label', children: 'children' },
  197. form: {
  198. code1: 0,
  199. layerVal: null,
  200. toward: null,
  201. shelfVal: null
  202. },
  203. selectList: [],
  204. options1: [
  205. { value: 0, label: '按 “排-单面-架” 筛选' },
  206. { value: 1, label: '按 “排-双面-架” 筛选' },
  207. { value: 2, label: '按 “排-单面” 筛选' },
  208. { value: 3, label: '按 “排-双面” 筛选' }
  209. ],
  210. layerOptions: [],
  211. abOptions: [],
  212. mapData: {},
  213. towardDisabled: false,
  214. currentTreeNode: null,
  215. is3DMap: false,
  216. rackOptions: [],
  217. bindAllRackList: [],
  218. bindAllInquiryList: [],
  219. filterShelfList: [],
  220. currentFid: null,
  221. // 绑定状态切换
  222. bindStatus: 'bound', // 'bound' | 'unbound'
  223. // 查询机筛选状态
  224. inquiryFilterStatus: 'all', // 'all' | 'bound' | 'unbound'
  225. // 模拟数据
  226. boundShelfList: [
  227. { id: 1, code: 'FTZN-03-001-A-01-1' },
  228. { id: 2, code: 'FTZN-03-001-A-01-2' },
  229. { id: 3, code: 'FTZN-03-001-A-01-3' },
  230. { id: 4, code: 'FTZN-03-001-A-01-4' },
  231. { id: 5, code: 'FTZN-03-001-A-01-5' },
  232. { id: 6, code: 'FTZN-03-001-A-01-6' },
  233. { id: 7, code: 'FTZN-03-001-A-01-7' },
  234. { id: 8, code: 'FTZN-03-001-A-01-8' }
  235. ],
  236. unboundShelfList: [
  237. { id: 1, code: 'FTZN-03-001-A-01-9' },
  238. { id: 2, code: 'FTZN-03-001-A-01-10' }
  239. ],
  240. // 查询机数据
  241. inquiryList: [
  242. { id: 1, name: '查询机001', bound: true, modelId: '7069948641322078208' },
  243. { id: 2, name: '查询机002', bound: false },
  244. { id: 3, name: '查询机003', bound: false },
  245. { id: 4, name: '查询机004', bound: false },
  246. { id: 5, name: '查询机005', bound: false },
  247. { id: 6, name: '查询机006', bound: false },
  248. { id: 7, name: '查询机007', bound: false },
  249. { id: 8, name: '查询机008', bound: false }
  250. ],
  251. bindData: {
  252. rackCount: 0,
  253. inquiryCount: 0,
  254. areaCount: 0
  255. },
  256. modelScreenPosition: null,
  257. // 对话框相关
  258. dialogVisible: false,
  259. dialogType: '', // 'rack' | 'inquiry' | 'area'
  260. dialogTitle: '',
  261. // 模拟数据
  262. rackDetails: [
  263. { id: 1, rack: 'FTZN-03-001-A-01-1', name: '架位1' },
  264. { id: 2, rack: 'FTZN-03-001-A-01-2', name: '架位2' },
  265. { id: 3, rack: 'FTZN-03-001-A-01-3', name: '架位3' },
  266. { id: 4, rack: 'FTZN-03-001-A-01-4', name: '架位4' },
  267. { id: 5, rack: 'FTZN-03-001-A-01-5', name: '架位5' },
  268. { id: 6, rack: 'FTZN-03-001-A-01-6', name: '架位6' },
  269. { id: 1, rack: 'FTZN-03-001-A-01-1', name: '架位1' },
  270. { id: 2, rack: 'FTZN-03-001-A-01-2', name: '架位2' },
  271. { id: 3, rack: 'FTZN-03-001-A-01-3', name: '架位3' },
  272. { id: 4, rack: 'FTZN-03-001-A-01-4', name: '架位4' },
  273. { id: 5, rack: 'FTZN-03-001-A-01-5', name: '架位5' },
  274. { id: 6, rack: 'FTZN-03-001-A-01-6', name: '架位6' }
  275. ],
  276. inquiryDetails: [
  277. { id: 1, name: '查询机001', ip: '192.168.1.100' }
  278. ],
  279. areaDetails: [
  280. { id: 1, code: '03', name: '区域1' }
  281. ]
  282. }
  283. },
  284. computed: {
  285. filteredInquiryList() {
  286. if (this.inquiryFilterStatus === 'bound') {
  287. return this.inquiryList.filter(item => item.bound)
  288. } else if (this.inquiryFilterStatus === 'unbound') {
  289. return this.inquiryList.filter(item => !item.bound)
  290. } else {
  291. return this.inquiryList
  292. }
  293. }
  294. },
  295. created() {
  296. FetchRegionTree().then(res => {
  297. this.regionTreeData = [this.transformData(res)]
  298. console.log(this.regionTreeData)
  299. this.$nextTick(() => {
  300. // 从localStorage获取并自动选中对应的节点
  301. this.autoSelectFromLocalStorage()
  302. // 如果没有localStorage数据,选择第一个有子节点的区域
  303. if (!this.regionTreeData[0].children?.length) return
  304. const targetFloor = this.regionTreeData[0].children.find(floor => floor.children?.length > 0)
  305. if (targetFloor?.children?.length && !this.currentTreeNode) {
  306. const firstRegion = targetFloor.children[0]
  307. this.$refs.treeRef.setCurrentKey(firstRegion.id)
  308. this.handleTreeNodeClick(firstRegion)
  309. }
  310. })
  311. }).catch(() => {
  312. })
  313. },
  314. mounted() {
  315. this.changeShelfToModel(this.options1[0]?.value)
  316. },
  317. methods: {
  318. getInitShelfGridByShelfId() {
  319. // rowType 1 单 2 双
  320. // toward 1 A面 2 B面
  321. // shelfType 1 '始终最左边为第1架(S型排架)'
  322. // shelfType 2 'A面最左为第1架(B面最左为最后1架)'
  323. // shelfType 3 'B面最左为第1架(A面最左为最后1架)'
  324. // floorType 1 '最顶层为第一层(从上至下)'
  325. // floorType 2 '最底层为第一层(从下至上)'
  326. console.log(' this.form', this.form)
  327. FetchInitShelfGridByShelfId({ 'shelfId': this.form.layerVal.id, 'toward': this.form.toward }).then(res => {
  328. console.log('FetchInitShelfGridByShelfId', res)
  329. this.filterShelfList = res
  330. }).catch(() => {
  331. })
  332. },
  333. // 从localStorage自动选择对应的节点和架位
  334. autoSelectFromLocalStorage() {
  335. try {
  336. const shelfDataStr = localStorage.getItem('shelf3dPositionRow')
  337. if (!shelfDataStr) return
  338. const shelfData = JSON.parse(shelfDataStr)
  339. console.log('shelf3dPositionRow', shelfData)
  340. // 1. 递归查找对应的区域节点
  341. const targetRegionNode = this.findRegionNodeByShelfData(this.regionTreeData, shelfData)
  342. if (targetRegionNode) {
  343. // 选中树形节点
  344. this.$refs.treeRef.setCurrentKey(targetRegionNode.id)
  345. // 触发节点点击事件
  346. this.handleTreeNodeClick(targetRegionNode)
  347. // 2. 自动填充表单值
  348. this.$nextTick(() => {
  349. this.autoFillForm(shelfData)
  350. })
  351. } else {
  352. console.warn('未找到对应的区域节点', shelfData)
  353. }
  354. } catch (error) {
  355. console.error('自动选择节点失败', error)
  356. }
  357. },
  358. // 递归查找对应的区域节点(根据floorName和regionName)
  359. findRegionNodeByShelfData(treeData, shelfData) {
  360. for (const node of treeData) {
  361. // 查找楼层节点
  362. if (node.floorId && node.raw?.floorName === shelfData.floorName) {
  363. // 遍历楼层下的区域节点
  364. if (node.children && node.children.length) {
  365. const regionNode = node.children.find(
  366. region => region.raw?.regionName === shelfData.regionName
  367. )
  368. if (regionNode) {
  369. return regionNode
  370. }
  371. }
  372. }
  373. // 递归查找子节点
  374. if (node.children && node.children.length) {
  375. const result = this.findRegionNodeByShelfData(node.children, shelfData)
  376. if (result) return result
  377. }
  378. }
  379. return null
  380. },
  381. // 根据架位数据自动填充表单
  382. autoFillForm(shelfData) {
  383. // 设置朝向
  384. this.form.toward = shelfData.toward
  385. // 根据rowType设置筛选类型
  386. if (shelfData.rowType === 1) {
  387. // 单面
  388. this.form.code1 = shelfData.shelfShelf > 1 ? 0 : 2 // 有架数选"排-单面-架",否则选"排-单面"
  389. } else if (shelfData.rowType === 2) {
  390. // 双面
  391. this.form.code1 = shelfData.shelfShelf > 1 ? 1 : 3 // 有架数选"排-双面-架",否则选"排-双面"
  392. this.towardDisabled = true
  393. }
  394. // 更新筛选类型对应的朝向选项
  395. this.changeShelfToModel(this.form.code1)
  396. // 在layerOptions加载完成后选择对应的排
  397. const timer = setInterval(() => {
  398. if (this.layerOptions.length > 0) {
  399. clearInterval(timer)
  400. // 查找对应的排
  401. const targetLayer = this.layerOptions.find(item => item.id === shelfData.shelfId)
  402. if (targetLayer) {
  403. this.form.layerVal = targetLayer
  404. // 触发架列表加载(包含联动逻辑)
  405. this.changeShelfGetGrid(targetLayer)
  406. }
  407. }
  408. }, 100)
  409. },
  410. transformData(data) {
  411. const rootNode = {
  412. id: data.fondsId,
  413. fondsId: data.fondsId,
  414. label: data.fondsName,
  415. raw: { ...data },
  416. children: data.floors.map(floor => {
  417. const floorNode = {
  418. id: floor.id,
  419. floorId: floor.id,
  420. label: floor.floorName,
  421. raw: { ...floor },
  422. children: floor.regions.map(region => {
  423. return {
  424. id: region.id,
  425. regionId: region.id,
  426. label: region.regionName,
  427. parentFloorId: floor.id,
  428. raw: { ...region }
  429. }
  430. })
  431. }
  432. return floorNode
  433. })
  434. }
  435. return rootNode
  436. },
  437. handleTreeNodeClick(nodeData) {
  438. this.form.layerVal = null
  439. this.form.shelfVal = null // 清空架位选择
  440. this.rackOptions = [] // 清空架位列表
  441. this.currentTreeNode = nodeData.raw
  442. console.log('handleTreeNodeClick', nodeData)
  443. const currentNode = this.$refs.treeRef.getNode(nodeData)
  444. console.log('currentNode', currentNode)
  445. if (currentNode.level === 1) {
  446. return
  447. }
  448. const parentNode = currentNode.parent
  449. const parentLabel = parentNode && parentNode.data && parentNode.data.label
  450. ? parentNode.data.label
  451. : ''
  452. this.selectedValue = parentLabel
  453. ? `${parentLabel} - ${nodeData.label}`
  454. : nodeData.label
  455. this.$refs.selectRef.blur()
  456. this.getInitBookShelfList(currentNode.data)
  457. if (nodeData.raw.mapType === 1) {
  458. this.is3DMap = false
  459. } else {
  460. this.is3DMap = true
  461. this.getMapDetails(nodeData.raw.mapId)
  462. }
  463. },
  464. getMapDetails(id) {
  465. const params = {
  466. 'id': id
  467. }
  468. FetchMapDetails(params).then(res => {
  469. this.mapData = res
  470. console.log('this.mapData', this.mapData)
  471. this.$nextTick(() => {
  472. if (this.$refs.map) {
  473. this.$refs.map.dispose()
  474. }
  475. if (this.currentTreeNode.mapLevel) {
  476. this.$refs.map.level = Number(this.currentTreeNode.mapLevel)
  477. } else {
  478. this.$refs.map.level = 1
  479. }
  480. if (this.currentTreeNode.fid) {
  481. this.$refs.map.areaFid = this.currentTreeNode.fid
  482. // this.handleAreaFid(this.currentTreeNode.fid)
  483. } else {
  484. this.$refs.map.areaFid = null
  485. this.currentFid = null
  486. }
  487. this.$refs.map.initMap()
  488. })
  489. }).catch(() => {
  490. })
  491. },
  492. changeShelfGetGrid(val) {
  493. if (!val) {
  494. this.rackOptions = []
  495. this.form.shelfVal = null
  496. return
  497. }
  498. FetchBookShelfDetails({ 'shelfId': val.id }).then(res => {
  499. this.rackOptions = []
  500. const start = parseInt(res.startShelf) || 1
  501. const end = start + res.shelfShelf - 1
  502. for (let i = start; i <= end; i++) {
  503. this.rackOptions.push({
  504. id: i,
  505. name: `${i.toString().padStart(2, '0')}`
  506. })
  507. }
  508. // 1. 根据选中排的rowType(单面/双面)自动设置筛选类型(code1)
  509. if (val.rowType === 1) { // 单面
  510. // 根据是否有多个架位,选择对应的筛选类型
  511. this.form.code1 = res.shelfShelf > 1 ? 0 : 2
  512. // 0: 按 "排-单面-架" 筛选 | 2: 按 "排-单面" 筛选
  513. } else if (val.rowType === 2) { // 双面
  514. this.form.code1 = res.shelfShelf > 1 ? 1 : 3
  515. // 1: 按 "排-双面-架" 筛选 | 3: 按 "排-双面" 筛选
  516. }
  517. this.changeShelfToModel(this.form.code1)
  518. // 4. 默认选中第一个架位(可选,根据业务需求调整)
  519. if (this.rackOptions.length > 0) {
  520. this.form.shelfVal = this.rackOptions[0].id
  521. } else {
  522. this.form.shelfVal = null
  523. }
  524. }).catch(() => {
  525. this.rackOptions = []
  526. this.form.shelfVal = null
  527. })
  528. },
  529. // 微调:筛选类型改变时,仅当当前朝向无效时才重置
  530. changeShelfToModel(val) {
  531. if (val === 1 || val === 3) {
  532. this.towardDisabled = true
  533. this.abOptions = [
  534. { value: 1, name: '双面' }
  535. ]
  536. } else {
  537. this.towardDisabled = false
  538. this.abOptions = [
  539. { value: 1, name: 'A面' },
  540. { value: 2, name: 'B面' }
  541. ]
  542. }
  543. // 只有当当前朝向不在可选范围内时,才重置朝向
  544. const hasCurrentToward = this.abOptions.some(item => item.value === this.form.toward)
  545. if (!hasCurrentToward) {
  546. this.form.toward = this.abOptions[0]?.value
  547. }
  548. },
  549. handleVisibleChange(visible) {
  550. // 下拉关闭时的额外逻辑(可选)
  551. },
  552. getInitBookShelfList(data) {
  553. const params = { 'floorId': data.parentFloorId, 'regionId': data.regionId }
  554. FetchInitBookShelfList(params).then(res => {
  555. if (res.content.length === 0) {
  556. this.layerOptions = []
  557. this.rackOptions = []
  558. } else {
  559. this.layerOptions = res.content.map(item => {
  560. return {
  561. name: item.shelfName,
  562. id: item.shelfId,
  563. toward: item.toward,
  564. rowType: item.rowType
  565. }
  566. })
  567. // 如果有localStorage数据,重新触发表单填充
  568. const shelfDataStr = localStorage.getItem('shelf3dPositionRow')
  569. if (shelfDataStr) {
  570. this.autoFillForm(JSON.parse(shelfDataStr))
  571. }
  572. }
  573. }).catch(() => {
  574. })
  575. },
  576. handleAreaFid(areaFid) {
  577. this.currentFid = areaFid
  578. console.log('mapAreaFid', areaFid)
  579. // 模拟获取绑定数据
  580. this.fetchBindData(areaFid)
  581. },
  582. fetchBindData(fid) {
  583. // 模拟异步请求
  584. setTimeout(() => {
  585. // 模拟数据,实际应该从后端接口获取
  586. this.bindData = {
  587. rackCount: 6, // 绑定架位数量
  588. inquiryCount: 1, // 绑定查询机数量
  589. areaCount: 1 // 绑定区域数量
  590. }
  591. }, 300)
  592. },
  593. // 显示详情对话框
  594. showDetail(type) {
  595. this.dialogType = type
  596. switch (type) {
  597. case 'rack':
  598. this.dialogTitle = `绑定架位详情 - 绑定位置【${this.currentFid}`
  599. break
  600. case 'inquiry':
  601. this.dialogTitle = `绑定查询机详情 - 绑定位置【${this.currentFid}`
  602. break
  603. case 'area':
  604. this.dialogTitle = `绑定区域详情 - 绑定位置【${this.currentFid}`
  605. break
  606. }
  607. this.dialogVisible = true
  608. },
  609. // 切换绑定状态
  610. switchBindStatus(status) {
  611. this.bindStatus = status
  612. // 这里后续可以添加后端接口调用,根据状态获取对应的数据
  613. // 例如:
  614. // if (status === 'bound') {
  615. // this.fetchBoundShelfList()
  616. // } else {
  617. // this.fetchUnboundShelfList()
  618. // }
  619. },
  620. // 处理查询机绑定
  621. handleInquiryBind(item) {
  622. this.$confirm('此操作将绑定所选查询机位置。<br>查询机一般作为3D地图导航的起点!<span>你是否还要继续?</span>', '提示', {
  623. confirmButtonText: '继续',
  624. cancelButtonText: '取消',
  625. type: 'warning',
  626. dangerouslyUseHTMLString: true
  627. }).then(() => {
  628. if (!this.currentFid) {
  629. this.$message.warning('请先选择一个模型位置')
  630. return
  631. }
  632. // 后续这里会调用后端接口进行绑定操作
  633. console.log('绑定查询机:', item.name, '到位置:', this.currentFid)
  634. // 模拟绑定成功
  635. item.bound = true
  636. item.modelId = this.currentFid
  637. this.$message.success('绑定成功')
  638. }).catch(err => {
  639. console.log(err)
  640. })
  641. },
  642. // 处理已绑定查询机的定位
  643. handleInquiryLocate(item) {
  644. if (!item.modelId) {
  645. this.$message.warning('该查询机未绑定模型位置')
  646. return
  647. }
  648. // 设置当前选中的FID,触发地图定位
  649. this.currentFid = item.modelId
  650. // 模拟获取绑定数据
  651. this.fetchBindData(item.modelId)
  652. // 调用地图组件的centerOnModel方法定位到指定模型
  653. this.$nextTick(() => {
  654. if (this.$refs.map) {
  655. this.$refs.map.centerOnModel(item.modelId)
  656. }
  657. })
  658. }
  659. }
  660. }
  661. </script>
  662. <style lang="scss" scoped>
  663. .tree-dropdown {
  664. width: 250px;
  665. max-height: 300px;
  666. overflow-y: auto;
  667. }
  668. .position-top{
  669. position: absolute;
  670. top: 10px;
  671. left: 20px;
  672. width: calc(100% - 40px);
  673. z-index: 999;
  674. }
  675. ::v-deep .el-tree{
  676. .el-tree-node__children{
  677. font-size: 14px;
  678. .iconfont{
  679. font-size: 12px;
  680. }
  681. }
  682. }
  683. .position-top-left,
  684. .position-top-right{
  685. width: 300px;
  686. }
  687. .shelf-bind-card{
  688. width: 280px;
  689. margin-top: 20px;
  690. font-size: 16px;
  691. .shelf-bind-tab{
  692. display: flex;
  693. justify-content: space-between;
  694. text-align: center;
  695. align-items: center;
  696. li{
  697. width: 50%;
  698. height: 40px;
  699. line-height: 40px;
  700. cursor: pointer;
  701. background: #f5f5f5;
  702. &.active{
  703. background: #fff;
  704. }
  705. }
  706. }
  707. }
  708. .shelf-bind-list{
  709. font-size: 14px;
  710. margin-top: 8px;
  711. height: 155px;
  712. overflow-y: scroll;
  713. li{
  714. display: flex;
  715. justify-content: flex-start;
  716. line-height: 30px;
  717. padding: 4px 10px;
  718. span{
  719. display: block;
  720. width: 30px;
  721. text-align: center;
  722. }
  723. p{
  724. flex: 1;
  725. padding-left: 20px;
  726. }
  727. &:hover{
  728. background: #f5f5f5;
  729. cursor: default;
  730. }
  731. }
  732. }
  733. .position-top-middle{
  734. width: 900px;
  735. .el-card{
  736. padding:20px 0 10px 0;
  737. }
  738. }
  739. .filter-shelf-list{
  740. border-top: 1px solid #edeff3;
  741. padding: 10px 10px 0 10px;
  742. height: 96px;
  743. overflow-y: scroll;
  744. .el-tag{
  745. margin-right: 5px;
  746. cursor: pointer;
  747. &:hover,
  748. &.bind-selected{
  749. background-color: #0348f3;
  750. color: #fff;
  751. border: 1px solid #0348f3;
  752. }
  753. }
  754. }
  755. .filter-model-select{
  756. border-top: 1px solid #edeff3;
  757. display: flex;
  758. justify-content: space-between;
  759. align-items: center;
  760. padding: 10px 10px 0 10px;
  761. margin-top: 10px;
  762. .model-select-style{
  763. font-weight: bold;
  764. &:first-child{
  765. flex: 1;
  766. }
  767. span{
  768. display: inline-block;
  769. width: 32px;
  770. height: 32px;
  771. line-height: 32px;
  772. background-color: #2ecaac;
  773. color: #fff;
  774. text-align: center;
  775. border-radius: 50%;
  776. }
  777. }
  778. .el-button{
  779. margin-left: 20px;
  780. }
  781. }
  782. .inquiry-machine-bind-top{
  783. display: flex;
  784. justify-content: space-between;
  785. align-items: center;
  786. color: #0c0e1e;
  787. border-bottom: 1px solid #edeff3;
  788. height: 40px;
  789. line-height: 40px;
  790. padding: 0 10px;
  791. .inquiry-machine-bind-params{
  792. font-size: 14px;
  793. }
  794. }
  795. /* 模型信息面板 */
  796. .model-info-panel{
  797. position: absolute;
  798. top: 42%;
  799. left: 50%;
  800. transform: translate(-50%, -50%);
  801. z-index: 1000;
  802. background-color: rgba(0, 0, 0, 0.7);
  803. border-radius: 8px;
  804. padding: 20px;
  805. color: #fff;
  806. box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.5);
  807. min-width: 300px;
  808. }
  809. .model-info-content{
  810. display: flex;
  811. flex-direction: column;
  812. gap: 10px;
  813. }
  814. .model-info-item{
  815. display: flex;
  816. align-items: center;
  817. gap: 10px;
  818. }
  819. .model-info-label{
  820. font-weight: bold;
  821. min-width: 100px;
  822. }
  823. .model-info-value{
  824. flex: 1;
  825. }
  826. .model-info-link{
  827. color: #409EFF !important;
  828. font-size: 12px;
  829. padding: 0 !important;
  830. margin: 0 !important;
  831. border: none !important;
  832. }
  833. .model-info-link:hover{
  834. color: #66B1FF !important;
  835. background-color: transparent !important;
  836. }
  837. .model-info-link:focus{
  838. color: #66B1FF !important;
  839. background-color: transparent !important;
  840. }
  841. </style>