wayline.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. <template>
  2. <div class="project-wayline-wrapper height-100">
  3. <a-spin :spinning="loading" :delay="300" tip="downloading" size="large">
  4. <div style="height: 50px; line-height: 50px; border-bottom: 1px solid #4f4f4f; font-weight: 450;">
  5. <a-row>
  6. <a-col :span="1"></a-col>
  7. <a-col :span="15">Flight Route Library</a-col>
  8. <a-col :span="8" v-if="importVisible" class="flex-row flex-justify-end flex-align-center">
  9. <a-upload name="file" :multiple="false" :before-upload="beforeUpload" :show-upload-list="false"
  10. :customRequest="uploadFile">
  11. <a-button type="text" style="color: white;">
  12. <SelectOutlined />
  13. </a-button>
  14. </a-upload>
  15. </a-col>
  16. </a-row>
  17. </div>
  18. <div :style="{ height: height + 'px' }" class="scrollbar">
  19. <div id="data" class="height-100 uranus-scrollbar" v-if="waylinesData.data.length !== 0" @scroll="onScroll">
  20. <div v-for="wayline in waylinesData.data" :key="wayline.id">
  21. <div class="wayline-panel" style="padding-top: 5px;" @click="selectRoute(wayline)">
  22. <div class="title">
  23. <a-tooltip :title="wayline.name">
  24. <div class="pr10"
  25. style="width: 120px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">{{
  26. wayline.name }}</div>
  27. </a-tooltip>
  28. <div class="ml10">
  29. <UserOutlined />
  30. </div>
  31. <a-tooltip :title="wayline.user_name">
  32. <div class="ml5 pr10"
  33. style="width: 80px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden;">{{
  34. wayline.user_name }}</div>
  35. </a-tooltip>
  36. <div class="fz20">
  37. <a-dropdown>
  38. <a style="color: white;">
  39. <EllipsisOutlined />
  40. </a>
  41. <template #overlay>
  42. <a-menu theme="dark" class="more" style="background: #3c3c3c;">
  43. <a-menu-item @click="downloadWayline(wayline.id, wayline.name)">
  44. <span>Download</span>
  45. </a-menu-item>
  46. <a-menu-item @click="showWaylineTip(wayline.id)">
  47. <span>Delete</span>
  48. </a-menu-item>
  49. </a-menu>
  50. </template>
  51. </a-dropdown>
  52. </div>
  53. </div>
  54. <div class="ml10 mt5" style="color: hsla(0,0%,100%,0.65);">
  55. <span>
  56. <RocketOutlined />
  57. </span>
  58. <span class="ml5">{{ DEVICE_NAME[wayline.drone_model_key] }}</span>
  59. <span class="ml10">
  60. <CameraFilled style="border-top: 1px solid; padding-top: -3px;" />
  61. </span>
  62. <span class="ml5" v-for="payload in wayline.payload_model_keys" :key="payload.id">
  63. {{ DEVICE_NAME[payload] }}
  64. </span>
  65. </div>
  66. <div class="mt5 ml10" style="color: hsla(0,0%,100%,0.35);">
  67. <span class="mr10">Update at {{ new Date(wayline.update_time).toLocaleString() }}</span>
  68. </div>
  69. </div>
  70. </div>
  71. </div>
  72. <div v-else>
  73. <a-empty :image-style="{ height: '60px', marginTop: '60px' }" />
  74. </div>
  75. <a-modal v-model:visible="deleteTip" width="450px" :closable="false" :maskClosable="false" centered
  76. :okButtonProps="{ danger: true }" @ok="deleteWayline">
  77. <p class="pt10 pl20" style="height: 50px;">Wayline file is unrecoverable once deleted. Continue?</p>
  78. <template #title>
  79. <div class="flex-row flex-justify-center">
  80. <span>Delete</span>
  81. </div>
  82. </template>
  83. </a-modal>
  84. </div>
  85. </a-spin>
  86. </div>
  87. </template>
  88. <script lang="ts" setup>
  89. import { message } from 'ant-design-vue'
  90. import { onMounted, reactive, ref } from 'vue'
  91. import { deleteWaylineFile, downloadWaylineFile, getWaylineFiles, importKmzFile } from '/@/api/wayline'
  92. import { ELocalStorageKey, ERouterName } from '/@/types'
  93. import { EllipsisOutlined, RocketOutlined, CameraFilled, UserOutlined, SelectOutlined } from '@ant-design/icons-vue'
  94. import { DEVICE_NAME } from '/@/types/device'
  95. import { useMyStore } from '/@/store'
  96. import { WaylineFile } from '/@/types/wayline'
  97. import { downloadFile } from '/@/utils/common'
  98. import { IPage } from '/@/api/http/type'
  99. import { load } from '@amap/amap-jsapi-loader'
  100. import { getRoot } from '/@/root'
  101. const loading = ref(false)
  102. const store = useMyStore()
  103. const pagination: IPage = {
  104. page: 1,
  105. total: -1,
  106. page_size: 10
  107. }
  108. const waylinesData = reactive({
  109. data: [] as WaylineFile[]
  110. })
  111. const root = getRoot()
  112. const workspaceId = localStorage.getItem(ELocalStorageKey.WorkspaceId)!
  113. const deleteTip = ref(false)
  114. const deleteWaylineId = ref<string>('')
  115. const canRefresh = ref(true)
  116. const importVisible = ref<boolean>(root.$router.currentRoute.value.name === ERouterName.WAYLINE)
  117. const height = ref()
  118. onMounted(() => {
  119. const parent = document.getElementsByClassName('scrollbar').item(0)?.parentNode as HTMLDivElement
  120. height.value = document.body.clientHeight - parent.firstElementChild!.clientHeight
  121. getWaylines()
  122. const key = setInterval(() => {
  123. const data = document.getElementById('data')?.lastElementChild as HTMLDivElement
  124. if (pagination.total === 0 || Math.ceil(pagination.total / pagination.page_size) <= pagination.page || height.value <= data?.clientHeight + data?.offsetTop) {
  125. clearInterval(key)
  126. return
  127. }
  128. pagination.page++
  129. getWaylines()
  130. }, 1000)
  131. })
  132. function getWaylines() {
  133. if (!canRefresh.value) {
  134. return
  135. }
  136. canRefresh.value = false
  137. getWaylineFiles(workspaceId, {
  138. page: pagination.page,
  139. page_size: pagination.page_size,
  140. order_by: 'update_time desc'
  141. }).then(res => {
  142. if (res.code !== 0) {
  143. return
  144. }
  145. waylinesData.data = [...waylinesData.data, ...res.data.list]
  146. pagination.total = res.data.pagination.total
  147. pagination.page = res.data.pagination.page
  148. }).finally(() => {
  149. canRefresh.value = true
  150. })
  151. }
  152. function showWaylineTip(waylineId: string) {
  153. deleteWaylineId.value = waylineId
  154. deleteTip.value = true
  155. }
  156. function deleteWayline() {
  157. deleteWaylineFile(workspaceId, deleteWaylineId.value).then(res => {
  158. if (res.code === 0) {
  159. message.success('Wayline file deleted')
  160. }
  161. deleteWaylineId.value = ''
  162. deleteTip.value = false
  163. pagination.total = 0
  164. pagination.page = 1
  165. waylinesData.data = []
  166. getWaylines()
  167. })
  168. }
  169. function downloadWayline(waylineId: string, fileName: string) {
  170. loading.value = true
  171. downloadWaylineFile(workspaceId, waylineId).then(res => {
  172. if (!res) {
  173. return
  174. }
  175. const data = new Blob([res], { type: 'application/zip' })
  176. downloadFile(data, fileName + '.kmz')
  177. }).finally(() => {
  178. loading.value = false
  179. })
  180. }
  181. function selectRoute(wayline: WaylineFile) {
  182. store.commit('SET_SELECT_WAYLINE_INFO', wayline)
  183. }
  184. function onScroll(e: any) {
  185. const element = e.srcElement
  186. if (element.scrollTop + element.clientHeight >= element.scrollHeight - 5 && Math.ceil(pagination.total / pagination.page_size) > pagination.page && canRefresh.value) {
  187. pagination.page++
  188. getWaylines()
  189. }
  190. }
  191. interface FileItem {
  192. uid: string;
  193. name?: string;
  194. status?: string;
  195. response?: string;
  196. url?: string;
  197. }
  198. interface FileInfo {
  199. file: FileItem;
  200. fileList: FileItem[];
  201. }
  202. const fileList = ref<FileItem[]>([])
  203. function beforeUpload(file: FileItem) {
  204. fileList.value = [file]
  205. loading.value = true
  206. return true
  207. }
  208. const uploadFile = async () => {
  209. fileList.value.forEach(async (file: FileItem) => {
  210. const fileData = new FormData()
  211. fileData.append('file', file, file.name)
  212. await importKmzFile(workspaceId, fileData).then((res) => {
  213. if (res.code === 0) {
  214. message.success(`${file.name} file uploaded successfully`)
  215. canRefresh.value = true
  216. pagination.total = 0
  217. pagination.page = 1
  218. waylinesData.data = []
  219. getWaylines()
  220. }
  221. }).finally(() => {
  222. loading.value = false
  223. fileList.value = []
  224. })
  225. })
  226. }
  227. </script>
  228. <style lang="scss" scoped>
  229. .wayline-panel {
  230. background: #3c3c3c;
  231. margin-left: auto;
  232. margin-right: auto;
  233. margin-top: 10px;
  234. height: 90px;
  235. width: 95%;
  236. font-size: 13px;
  237. border-radius: 2px;
  238. cursor: pointer;
  239. .title {
  240. display: flex;
  241. flex-direction: row;
  242. align-items: center;
  243. height: 30px;
  244. font-weight: bold;
  245. margin: 0px 10px 0 10px;
  246. }
  247. }
  248. .uranus-scrollbar {
  249. overflow: auto;
  250. scrollbar-width: thin;
  251. scrollbar-color: #c5c8cc transparent;
  252. }
  253. </style>