index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <template>
  2. <div>
  3. <el-card class="list-query" shadow="hover">
  4. <el-form inline label-width="150px">
  5. <el-form-item :label="T('LastOnlineTime')">
  6. <el-select v-model="listQuery.time_ago" clearable>
  7. <el-option
  8. v-for="item in timeFilters"
  9. :key="item.value"
  10. :label="item.text"
  11. :value="item.value"
  12. :disabled="item.value === 0"
  13. ></el-option>
  14. </el-select>
  15. </el-form-item>
  16. <el-form-item>
  17. <el-button type="primary" @click="handlerQuery">{{ T('Filter') }}</el-button>
  18. <el-button type="danger" @click="toAdd">{{ T('Add') }}</el-button>
  19. <el-button type="success" @click="toExport">{{ T('Export') }}</el-button>
  20. <el-button type="danger" @click="toBatchDelete">{{ T('BatchDelete') }}</el-button>
  21. </el-form-item>
  22. </el-form>
  23. </el-card>
  24. <el-card class="list-body" shadow="hover">
  25. <el-table :data="listRes.list" v-loading="listRes.loading" border size="small" @selection-change="handleSelectionChange">
  26. <el-table-column type="selection" width="55" align="center"/>
  27. <el-table-column prop="id" label="id" align="center"/>
  28. <el-table-column prop="cpu" label="cpu" align="center"/>
  29. <el-table-column prop="hostname" :label="T('Hostname')" align="center"/>
  30. <el-table-column prop="memory" :label="T('Memory')" align="center"/>
  31. <el-table-column prop="os" :label="T('Os')" align="center"/>
  32. <el-table-column prop="username" :label="T('Username')" align="center"/>
  33. <el-table-column prop="uuid" :label="T('Uuid')" align="center"/>
  34. <el-table-column prop="version" :label="T('Version')" align="center" width="80"/>
  35. <el-table-column prop="created_at" :label="T('CreatedAt')" align="center"/>
  36. <el-table-column prop="updated_at" :label="T('UpdatedAt')" align="center"/>
  37. <el-table-column prop="last_online_time" :label="T('LastOnlineTime')" align="center">
  38. <template #default="{row}">
  39. <div class="last_oline_time">
  40. <span> {{ row.last_online_time ? timeAgo(row.last_online_time * 1000) : '-' }}</span> <span class="dot" :class="{red: timeDis(row.last_online_time) >= 60, green: timeDis(row.last_online_time)< 60}"></span>
  41. </div>
  42. </template>
  43. </el-table-column>
  44. <el-table-column :label="T('Actions')" align="center" width="500">
  45. <template #default="{row}">
  46. <el-button type="success" @click="toWebClientLink(row)">Web-Client</el-button>
  47. <el-button type="primary" @click="toAddressBook(row)">{{ T('AddToAddressBook') }}</el-button>
  48. <el-button @click="toEdit(row)">{{ T('Edit') }}</el-button>
  49. <el-button type="danger" @click="del(row)">{{ T('Delete') }}</el-button>
  50. </template>
  51. </el-table-column>
  52. </el-table>
  53. </el-card>
  54. <el-card class="list-page" shadow="hover">
  55. <el-pagination background
  56. layout="prev, pager, next, sizes, jumper"
  57. :page-sizes="[10,20,50,100]"
  58. v-model:page-size="listQuery.page_size"
  59. v-model:current-page="listQuery.page"
  60. :total="listRes.total">
  61. </el-pagination>
  62. </el-card>
  63. <el-dialog v-model="formVisible" :title="!formData.row_id?T('Create'):T('Update')" width="800">
  64. <el-form class="dialog-form" ref="form" :model="formData" label-width="120px">
  65. <el-form-item label="id" prop="id" required>
  66. <el-input v-model="formData.id"></el-input>
  67. </el-form-item>
  68. <el-form-item :label="T('Username')" prop="username">
  69. <el-input v-model="formData.username"></el-input>
  70. </el-form-item>
  71. <el-form-item :label="T('Hostname')" prop="hostname">
  72. <el-input v-model="formData.hostname"></el-input>
  73. </el-form-item>
  74. <el-form-item label="cpu" prop="cpu">
  75. <el-input v-model="formData.cpu"></el-input>
  76. </el-form-item>
  77. <el-form-item :label="T('Memory')" prop="memory">
  78. <el-input v-model="formData.memory"></el-input>
  79. </el-form-item>
  80. <el-form-item :label="T('Os')" prop="os">
  81. <el-input v-model="formData.os"></el-input>
  82. </el-form-item>
  83. <el-form-item :label="T('Uuid')" prop="uuid">
  84. <el-input v-model="formData.uuid"></el-input>
  85. </el-form-item>
  86. <el-form-item :label="T('Version')" prop="version">
  87. <el-input v-model="formData.version"></el-input>
  88. </el-form-item>
  89. <el-form-item>
  90. <el-button @click="formVisible = false">{{ T('Cancel') }}</el-button>
  91. <el-button @click="submit" type="primary">{{ T('Submit') }}</el-button>
  92. </el-form-item>
  93. </el-form>
  94. </el-dialog>
  95. <el-dialog v-model="ABFormVisible" width="800" :title="T('Create')">
  96. <el-form class="dialog-form" ref="form" :model="ABFormData" label-width="120px">
  97. <el-form-item :label="T('Owner')" prop="user_ids" required>
  98. <el-select v-model="ABFormData.user_ids" multiple>
  99. <el-option
  100. v-for="item in allUsers"
  101. :key="item.id"
  102. :label="item.username"
  103. :value="item.id"
  104. ></el-option>
  105. </el-select>
  106. </el-form-item>
  107. <el-form-item label="id" prop="id" required>
  108. <el-input v-model="ABFormData.id"></el-input>
  109. </el-form-item>
  110. <el-form-item :label="T('Username')" prop="username">
  111. <el-input v-model="ABFormData.username"></el-input>
  112. </el-form-item>
  113. <el-form-item :label="T('Alias')" prop="alias">
  114. <el-input v-model="ABFormData.alias"></el-input>
  115. </el-form-item>
  116. <el-form-item :label="T('Hostname')" prop="hostname">
  117. <el-input v-model="ABFormData.hostname"></el-input>
  118. </el-form-item>
  119. <el-form-item :label="T('Platform')" prop="platform">
  120. <el-select v-model="ABFormData.platform">
  121. <el-option
  122. v-for="item in ABPlatformList"
  123. :key="item.value"
  124. :label="item.label"
  125. :value="item.value"
  126. ></el-option>
  127. </el-select>
  128. </el-form-item>
  129. <el-form-item :label="T('Tags')" prop="tags">
  130. <el-select v-model="ABFormData.tags" multiple>
  131. <el-option
  132. v-for="item in tagList"
  133. :key="item.name"
  134. :label="item.name"
  135. :value="item.name"
  136. ></el-option>
  137. </el-select>
  138. </el-form-item>
  139. <el-form-item>
  140. <el-button @click="ABFormVisible = false">{{ T('Cancel') }}</el-button>
  141. <el-button @click="ABSubmit" type="primary">{{ T('Submit') }}</el-button>
  142. </el-form-item>
  143. </el-form>
  144. </el-dialog>
  145. </div>
  146. </template>
  147. <script setup>
  148. import { computed, onActivated, onMounted, reactive, ref, watch } from 'vue'
  149. import { batchRemove, create, list, remove, update } from '@/api/peer'
  150. import { ElMessage, ElMessageBox } from 'element-plus'
  151. import { toWebClientLink } from '@/utils/webclient'
  152. import { T } from '@/utils/i18n'
  153. import { timeAgo } from '@/utils/time'
  154. import { jsonToCsv, downBlob } from '@/utils/file'
  155. import { useRepositories as useABRepositories } from '@/views/address_book/index'
  156. import { loadAllUsers } from '@/global'
  157. import { list as fetchTagList } from '@/api/tag'
  158. import { batchCreate } from '@/api/address_book'
  159. const listRes = reactive({
  160. list: [], total: 0, loading: false,
  161. })
  162. const listQuery = reactive({
  163. page: 1,
  164. page_size: 10,
  165. time_ago: null,
  166. })
  167. const getList = async () => {
  168. listRes.loading = true
  169. const res = await list(listQuery).catch(_ => false)
  170. listRes.loading = false
  171. if (res) {
  172. listRes.list = res.data.list
  173. listRes.total = res.data.total
  174. }
  175. }
  176. const handlerQuery = () => {
  177. if (listQuery.page === 1) {
  178. getList()
  179. } else {
  180. listQuery.page = 1
  181. }
  182. }
  183. const del = async (row) => {
  184. const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('Delete') }), {
  185. confirmButtonText: T('Confirm'),
  186. cancelButtonText: T('Cancel'),
  187. type: 'warning',
  188. }).catch(_ => false)
  189. if (!cf) {
  190. return false
  191. }
  192. const res = await remove({ row_id: row.row_id }).catch(_ => false)
  193. if (res) {
  194. ElMessage.success(T('OperationSuccess'))
  195. getList()
  196. }
  197. }
  198. onMounted(getList)
  199. onActivated(getList)
  200. watch(() => listQuery.page, getList)
  201. watch(() => listQuery.page_size, handlerQuery)
  202. const formVisible = ref(false)
  203. const formData = reactive({
  204. row_id: 0,
  205. cpu: '',
  206. hostname: '',
  207. id: '',
  208. memory: '',
  209. os: '',
  210. username: '',
  211. uuid: '',
  212. version: '',
  213. })
  214. const toEdit = (row) => {
  215. formVisible.value = true
  216. //将row中的数据赋值给formData
  217. Object.keys(formData).forEach(key => {
  218. formData[key] = row[key]
  219. })
  220. }
  221. const toAdd = () => {
  222. formVisible.value = true
  223. //重置formData
  224. formData.row_id = 0
  225. formData.cpu = ''
  226. formData.hostname = ''
  227. formData.id = ''
  228. formData.memory = ''
  229. formData.os = ''
  230. formData.username = ''
  231. formData.uuid = ''
  232. formData.version = ''
  233. }
  234. const submit = async () => {
  235. const api = formData.row_id ? update : create
  236. const res = await api(formData).catch(_ => false)
  237. if (res) {
  238. ElMessage.success(T('OperationSuccess'))
  239. formVisible.value = false
  240. getList()
  241. }
  242. }
  243. const timeDis = (time) => {
  244. let now = new Date().getTime()
  245. let after = new Date(time * 1000).getTime()
  246. return (now - after) / 1000
  247. }
  248. const timeFilters = computed(() => [
  249. { text: T('MinutesLess', { param: 1 }, 1), value: -60 },
  250. { text: T('HoursLess', { param: 1 }, 1), value: -3600 },
  251. { text: T('DaysLess', { param: 1 }, 1), value: -86400 },
  252. { text: '---------', value: 0 },
  253. { text: T('MinutesAgo', { param: 1 }, 1), value: 60 },
  254. { text: T('HoursAgo', { param: 1 }, 1), value: 3600 },
  255. { text: T('DaysAgo', { param: 1 }, 1), value: 86400 },
  256. { text: T('MonthsAgo', { param: 1 }, 1), value: 2592000 },
  257. // { text: T('YearsAgo', { param: 1 }, 1), value: 31536000 },
  258. ])
  259. const toExport = async () => {
  260. const q = { ...listQuery }
  261. q.page_size = 10000
  262. q.page = 1
  263. const res = await list(q).catch(_ => false)
  264. if (res) {
  265. const data = res.data.list.map(item => {
  266. item.last_online_time = item.last_online_time ? new Date(item.last_online_time * 1000).toLocaleString() : '-'
  267. return item
  268. })
  269. const csv = jsonToCsv(data)
  270. downBlob(csv, 'peers.csv')
  271. }
  272. }
  273. const {
  274. platformList: ABPlatformList,
  275. formVisible: ABFormVisible,
  276. formData: ABFormData,
  277. } = useABRepositories()
  278. const toAddressBook = (peer) => {
  279. ABFormData.id = peer.id
  280. ABFormData.username = peer.username
  281. ABFormData.hostname = peer.hostname
  282. //匹配os
  283. if (peer.os.indexOf('windows') !== -1) {
  284. ABFormData.platform = ABPlatformList.find(item => item.label === 'Windows').value
  285. } else if (peer.os.indexOf('linux') !== -1) {
  286. ABFormData.platform = ABPlatformList.find(item => item.label === 'Linux').value
  287. } else if (peer.os.indexOf('android') !== -1) {
  288. ABFormData.platform = ABPlatformList.find(item => item.label === 'Android').value
  289. } else if (peer.os.indexOf('mac') !== -1) {
  290. ABFormData.platform = ABPlatformList.find(item => item.label === 'Mac OS').value
  291. }
  292. ABFormData.uuid = peer.uuid
  293. ABFormVisible.value = true
  294. }
  295. const ABSubmit = async () => {
  296. const res = await batchCreate(ABFormData).catch(_ => false)
  297. if (res) {
  298. ElMessage.success(T('OperationSuccess'))
  299. ABFormVisible.value = false
  300. }
  301. }
  302. const { allUsers, getAllUsers } = loadAllUsers()
  303. const tagList = ref([])
  304. const fetchTagListData = async (user_id) => {
  305. const res = await fetchTagList({ user_id }).catch(_ => false)
  306. if (res) {
  307. const ls = []
  308. res.data.list.map(item => {
  309. if (!ls.includes(item.name)) {
  310. ls.push(item.name)
  311. }
  312. })
  313. tagList.value = ls.map(item => ({ name: item }))
  314. }
  315. }
  316. onMounted(getAllUsers)
  317. onMounted(fetchTagListData)
  318. const multipleSelection = ref([])
  319. const handleSelectionChange = (val) => {
  320. multipleSelection.value = val
  321. }
  322. const toBatchDelete = async () => {
  323. if (!multipleSelection.value.length) {
  324. ElMessage.warning(T('PleaseSelectData'))
  325. return false
  326. }
  327. const cf = await ElMessageBox.confirm(T('Confirm?', { param: T('BatchDelete') }), {
  328. confirmButtonText: T('Confirm'),
  329. cancelButtonText: T('Cancel'),
  330. type: 'warning',
  331. }).catch(_ => false)
  332. if (!cf) {
  333. return false
  334. }
  335. const res = await batchRemove({ row_ids: multipleSelection.value.map(i => i.row_id) }).catch(_ => false)
  336. if (res) {
  337. ElMessage.success(T('OperationSuccess'))
  338. getList()
  339. }
  340. }
  341. </script>
  342. <style scoped lang="scss">
  343. .list-query .el-select {
  344. --el-select-width: 180px;
  345. }
  346. .last_oline_time {
  347. display: flex;
  348. justify-content: center;
  349. align-items: center;
  350. }
  351. .dot {
  352. width: 6px;
  353. height: 6px;
  354. display: block;
  355. border-radius: 50%;
  356. margin-left: 10px;
  357. &.red {
  358. background-color: red;
  359. }
  360. &.green {
  361. background-color: green;
  362. }
  363. }
  364. </style>