index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. <template>
  2. <div class="bs-container">
  3. <div class="inner-container">
  4. <el-form
  5. :inline="true"
  6. class="filter-container"
  7. >
  8. <el-form-item class="filter-input filter-item">
  9. <el-input
  10. v-model="searchForm.searchKey"
  11. class="bs-el-input"
  12. clearable
  13. maxlength="200"
  14. @clear="getDataList"
  15. placeholder="请输入地图名称或标识"
  16. />
  17. </el-form-item>
  18. <el-form-item class="filter-item">
  19. <el-select
  20. v-model="searchForm.level"
  21. class="bs-el-select"
  22. clearable
  23. placeholder="请选择地图级别"
  24. popper-class="bs-el-select"
  25. @change="getDataList"
  26. >
  27. <el-option
  28. v-for="level in levelList"
  29. :key="level.value"
  30. :label="level.label"
  31. :value="level.value"
  32. />
  33. </el-select>
  34. </el-form-item>
  35. <el-form-item class="filter-item">
  36. <el-button
  37. :loading="searchLoading"
  38. icon="el-icon-search"
  39. type="primary"
  40. @click="getDataList"
  41. >
  42. 查询
  43. </el-button>
  44. </el-form-item>
  45. <el-form-item class="filter-item">
  46. <el-button
  47. class="bs-el-button-default"
  48. @click="addMap"
  49. >
  50. 新增
  51. </el-button>
  52. </el-form-item>
  53. </el-form>
  54. <div class="bs-table-box">
  55. <el-table
  56. v-loading="searchLoading"
  57. ref="table"
  58. v-table
  59. :data="mapList"
  60. :element-loading-text="loadingText"
  61. :load="load"
  62. :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
  63. class="bs-el-table bs-scrollbar"
  64. height="0"
  65. lazy
  66. row-key="id"
  67. >
  68. <el-empty slot="empty"/>
  69. <el-table-column
  70. align="left"
  71. label="地图名称"
  72. prop="name"
  73. show-overflow-tooltip
  74. />
  75. <el-table-column
  76. align="center"
  77. label="地图标识"
  78. prop="mapCode"
  79. show-overflow-tooltip
  80. />
  81. <el-table-column
  82. align="center"
  83. label="地图级别"
  84. prop="level"
  85. show-overflow-tooltip
  86. >
  87. <template slot-scope="scope">
  88. <span v-if="scope.row.level === 0">世界</span>
  89. <span v-else-if="scope.row.level === 1">国家</span>
  90. <span v-else-if="scope.row.level === 2">省份</span>
  91. <span v-else-if="scope.row.level === 3">城市</span>
  92. <span v-else-if="scope.row.level === 4">区县</span>
  93. <span v-else>{{ getMoreLevel(scope.row.level) }}</span>
  94. </template>
  95. </el-table-column>
  96. <el-table-column
  97. align="center"
  98. label="已上传配置JSON"
  99. prop="uploadedGeoJson"
  100. show-overflow-tooltip
  101. >
  102. <template slot-scope="scope">
  103. <span v-if="scope.row.uploadedGeoJson === 1">是</span>
  104. <span v-else>否</span>
  105. </template>
  106. </el-table-column>
  107. <el-table-column
  108. align="center"
  109. label="操作"
  110. width="200"
  111. >
  112. <template slot-scope="scope">
  113. <el-button
  114. class="bs-el-button-default"
  115. @click="editMap(scope.row)"
  116. >
  117. 编辑
  118. </el-button>
  119. <el-button
  120. class="bs-el-button-default"
  121. @click="deleteMap(scope.row)"
  122. >
  123. 删除
  124. </el-button>
  125. <el-button
  126. v-if="scope.row.uploadedGeoJson === 1"
  127. class="bs-el-button-default"
  128. @click="addChild(scope.row)"
  129. >
  130. 添加下级
  131. </el-button>
  132. <el-button
  133. v-if="scope.row.uploadedGeoJson === 0"
  134. class="bs-el-button-default"
  135. @click="uploadGeoJson(scope.row)"
  136. >
  137. 上传配置
  138. </el-button>
  139. </template>
  140. </el-table-column>
  141. </el-table>
  142. </div>
  143. </div>
  144. <add-form
  145. ref="addForm"
  146. @refresh="refreshData"
  147. />
  148. <edit-form
  149. ref="editForm"
  150. @refresh="refreshData"
  151. />
  152. <el-dialog
  153. :close-on-click-modal="false"
  154. :visible.sync="geoJsonVisible"
  155. append-to-body
  156. class="bs-dialog-wrap bs-el-dialog"
  157. height="1000px"
  158. title="geoJson数据"
  159. width="1000px"
  160. >
  161. <vue-json-viewer
  162. v-model="currentMapGeoJSon"
  163. theme="dark"
  164. :show-btns="false"
  165. mode="code"
  166. />
  167. <el-button
  168. class="bs-el-button-default"
  169. @click="upload()"
  170. >
  171. <i class="el-icon-upload2"></i>
  172. 上传
  173. </el-button>
  174. <span
  175. slot="footer"
  176. class="dialog-footer"
  177. >
  178. <el-button
  179. class="bs-el-button-default"
  180. @click="submitUpload"
  181. >提交</el-button>
  182. </span>
  183. </el-dialog>
  184. <input
  185. ref="geoJsonFileUpload"
  186. accept=".json"
  187. name="file"
  188. style="display: none"
  189. type="file"
  190. @change="handleBatchUpload"
  191. >
  192. </div>
  193. </template>
  194. <script>
  195. import table from 'data-room-ui/js/utils/table.js'
  196. import {mapList, mapDelete, uploadGeoJson, mapCascadeDelete, mapInfo} from 'data-room-ui/js/utils/mapDataService'
  197. import AddForm from "./AddForm"
  198. import EditForm from "./EditForm"
  199. import vueJsonViewer from 'vue-json-viewer'
  200. export default {
  201. name: "MapManagement",
  202. directives: {
  203. table // 注册自定义指令
  204. },
  205. components: {
  206. AddForm,
  207. EditForm,
  208. vueJsonViewer
  209. },
  210. data() {
  211. return {
  212. currentMap: {}, // 当前操作的地图
  213. currentMapGeoJSon: {}, // 当前操作的地图geoJson
  214. loadingText: '',
  215. searchLoading: false,
  216. geoJsonVisible: false,
  217. lazyResolveIds: [],
  218. lazyResolveMap: new Map(),
  219. searchForm: {
  220. searchKey: '',
  221. level: null,
  222. uploadedGeoJson: null,
  223. parentId: '0'
  224. },
  225. levelList: [
  226. {
  227. label: '世界',
  228. value: 0
  229. },
  230. {
  231. label: '国家',
  232. value: 1
  233. },
  234. {
  235. label: '省份',
  236. value: 2
  237. },
  238. {
  239. label: '城市',
  240. value: 3
  241. },
  242. {
  243. label: '区县',
  244. value: 4
  245. }
  246. ],
  247. mapList: [],
  248. // 提示过了
  249. tipped: false
  250. }
  251. },
  252. mounted() {
  253. this.init()
  254. },
  255. methods: {
  256. init() {
  257. this.searchLoading = true
  258. this.loadingText = '正在加载地图数据...'
  259. mapList(this.searchForm).then(res => {
  260. this.mapList = res
  261. this.searchLoading = false
  262. if (!this.tipped && this.mapList.length === 0) {
  263. this.tip()
  264. }
  265. }).catch(err => {
  266. this.searchLoading = false
  267. })
  268. },
  269. getDataList() {
  270. this.lazyResolveMap.clear()
  271. this.$nextTick(() => {
  272. this.mapList = []
  273. })
  274. this.$nextTick(() => {
  275. this.searchLoading = true
  276. this.loadingText = '正在加载地图数据...'
  277. mapList(this.searchForm).then(res => {
  278. this.mapList = res
  279. this.searchLoading = false
  280. }).catch(err => {
  281. this.searchLoading = false
  282. })
  283. // 清除展开状态
  284. for (let i = 0; i < this.lazyResolveIds.length; i++) {
  285. this.$refs.table.store.states.treeData[this.lazyResolveIds[i]].loaded = false;
  286. this.$refs.table.store.states.treeData[this.lazyResolveIds[i]].expanded = false
  287. }
  288. })
  289. },
  290. /**
  291. * 新增、删除、修改等操作成功后刷新数据,不改变展开状态
  292. * @param cbObj
  293. */
  294. refreshData(cbObj) {
  295. let parentId = cbObj.parentId
  296. if (this.lazyResolveMap.get(parentId)) {
  297. // 刷新父节点
  298. const { data, treeNode, resolve } = this.lazyResolveMap.get(parentId)
  299. this.$set(this.$refs.table.store.states.lazyTreeNodeMap, parentId, [])
  300. this.load(data, treeNode, resolve)
  301. return
  302. }
  303. if (parentId === '0' || parentId === 0) {
  304. // 刷新根节点
  305. this.getDataList()
  306. return
  307. }
  308. mapInfo(parentId).then((res) => {
  309. parentId = res.parentId
  310. if (this.lazyResolveMap.get(parentId)) {
  311. // 刷新父节点的父节点
  312. const { data, treeNode, resolve } = this.lazyResolveMap.get(parentId)
  313. this.$set(this.$refs.table.store.states.lazyTreeNodeMap, parentId, [])
  314. this.load(data, treeNode, resolve)
  315. } else {
  316. // 刷新根节点
  317. this.getDataList()
  318. }
  319. })
  320. },
  321. getMoreLevel(level) {
  322. return '级别' + (level + 1)
  323. },
  324. addMap() {
  325. this.$refs.addForm.mapFormVisible = true
  326. this.$refs.addForm.init()
  327. },
  328. load(data, treeNode, resolve) {
  329. this.lazyResolveMap.set(data.id, { data, treeNode, resolve })
  330. this.lazyResolveIds.push(data.id)
  331. mapList({
  332. parentId: data.id
  333. }).then(childList => {
  334. // 解决同一页中同一条数据同时出现,如果懒加载中存在,那么将之前查询出来的数据删除
  335. let deleteIdList = []
  336. childList.forEach((child) => {
  337. this.mapList.forEach((mapInfo) => {
  338. if (mapInfo.id === child.id) {
  339. deleteIdList.push(mapInfo.id)
  340. }
  341. })
  342. })
  343. this.mapList = this.mapList.filter((map) => {
  344. return deleteIdList.indexOf(map.id) === -1
  345. })
  346. resolve(childList)
  347. }).catch(err => {
  348. resolve([])
  349. })
  350. },
  351. deleteMap(map) {
  352. this.$confirm('确定删除该地图?', '提示', {
  353. confirmButtonText: '确定',
  354. cancelButtonText: '取消',
  355. type: 'warning',
  356. customClass: 'bs-el-message-box'
  357. }).then(async () => {
  358. mapDelete(map.id).then((deleteSuccess) => {
  359. if (deleteSuccess) {
  360. this.$message({
  361. type: 'success',
  362. message: '删除成功'
  363. })
  364. this.refreshData({
  365. id: map.id,
  366. parentId: map.parentId
  367. })
  368. } else {
  369. this.deleteMapCascade(map)
  370. }
  371. }).catch(() => {
  372. this.$message({
  373. type: 'error',
  374. message: '删除失败'
  375. })
  376. })
  377. }).catch(() => {
  378. })
  379. },
  380. tip() {
  381. // 超链接跳转至
  382. const htmlStr = `<span>大屏设计器提供了全国省市区县的地图数据,<a href="https://www.yuque.com/chuinixiongkou/bigscreen/kdrm8g3c8zfgaaq6#xjE8w" style="color: #00a0e9" target="_blank">点击查看</a></span>`
  383. this.$notify({
  384. title: '推荐',
  385. dangerouslyUseHTMLString: true,
  386. message: htmlStr,
  387. customClass: 'ds-el-notify',
  388. type: 'warning',
  389. duration: 5000
  390. })
  391. },
  392. deleteMapCascade(map) {
  393. this.$confirm('该地图存在子级,是否直接删除该地图以及其所有子级?', '提示', {
  394. confirmButtonText: '确定',
  395. cancelButtonText: '取消',
  396. type: 'warning',
  397. customClass: 'bs-el-message-box'
  398. }).then(async () => {
  399. mapCascadeDelete(map.id).then(() => {
  400. this.$message({
  401. type: 'success',
  402. message: '删除成功'
  403. })
  404. this.refreshData({
  405. id: map.id,
  406. parentId: map.parentId
  407. })
  408. }).catch(() => {
  409. })
  410. }).catch(() => {
  411. })
  412. },
  413. addChild(map) {
  414. this.$refs.addForm.mapFormVisible = true
  415. this.$refs.addForm.init(map)
  416. },
  417. editMap(map) {
  418. this.$refs.editForm.mapFormVisible = true
  419. this.$refs.editForm.init(map)
  420. },
  421. uploadGeoJson(map) {
  422. this.currentMap = map
  423. this.currentMapGeoJSon = {}
  424. this.geoJsonVisible = true
  425. },
  426. upload() {
  427. this.$refs.geoJsonFileUpload.click()
  428. },
  429. handleBatchUpload(source) {
  430. this.uploadLoading = true
  431. const file = source.target.files
  432. const reader = new FileReader() // 新建一个FileReader
  433. reader.readAsText(file[0], 'UTF-8') // 读取文件
  434. reader.onload = (event) => {
  435. let jsonStr = event.target.result
  436. // 读取文件内容
  437. try {
  438. this.currentMapGeoJSon = JSON.parse(jsonStr)
  439. } catch (e) {
  440. this.uploadLoading = false
  441. this.$message.error('JSON文件格式错误')
  442. return false
  443. }
  444. this.uploadLoading = false
  445. // input通过onchange事件来触发js代码的,由于两次文件是重复的,所以这个时候onchange事件是没有触发到的,所以需要手动清空input的值
  446. source.target.value = ''
  447. }
  448. },
  449. submitUpload() {
  450. // 先检查JSON是否合法
  451. if (typeof this.currentMapGeoJSon === 'string') {
  452. this.$message.error('JSON文件格式错误')
  453. return false
  454. }
  455. if (this.currentMapGeoJSon === {}) {
  456. this.$message.error('JSON数据不能为空')
  457. return false
  458. }
  459. // 调接口保存
  460. uploadGeoJson({
  461. id: this.currentMap.id,
  462. geoJson: JSON.stringify(this.currentMapGeoJSon)
  463. }).then(res => {
  464. this.$message({
  465. type: 'success',
  466. message: '上传成功'
  467. })
  468. this.geoJsonVisible = false
  469. // 刷新
  470. this.refreshData({
  471. id: this.currentMap.id,
  472. parentId: this.currentMap.parentId
  473. })
  474. }).catch(err => {
  475. this.$message({
  476. type: 'error',
  477. message: '上传失败'
  478. })
  479. })
  480. this.geoJsonVisible = false
  481. },
  482. isWhitespace(str) {
  483. // 如果是null、undefined,返回true
  484. if (str == null) {
  485. return true
  486. }
  487. return /^\s*$/.test(str);
  488. },
  489. },
  490. }
  491. </script>
  492. <style lang="scss" scoped>
  493. @import '../../assets/style/bsTheme.scss';
  494. .jv-container.dark {
  495. color: aliceblue;
  496. background: #161A26;
  497. height: 150px;
  498. }
  499. </style>
  500. <style lang="scss">
  501. //修改notify的样式
  502. .ds-el-notify {
  503. background-color: var(--bs-el-background-1)!important;
  504. border: var(--bs-el-border)!important;
  505. .el-notification__title{
  506. color: #fff!important;
  507. }
  508. .el-notification__content{
  509. color: #fff!important;
  510. }
  511. .el-notification__closeBtn{
  512. color: #fff!important;
  513. }
  514. }
  515. </style>