index.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <template>
  2. <div>
  3. <div class="content-box chatdoom page">
  4. <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick">
  5. <!-- 日志列表 -->
  6. <el-tab-pane label="服务日志" name="1">
  7. <el-card shadow="hover">
  8. <el-table ref="table" :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
  9. <el-table-column prop="name" label="文件名" show-overflow-tooltip></el-table-column>
  10. <el-table-column prop="size" label="大小" show-overflow-tooltip></el-table-column>
  11. <el-table-column prop="changeAt" label="修改时间" min-width="100" align="center"></el-table-column>
  12. <el-table-column label="操作" width="200" align="center">
  13. <template #default="scope">
  14. <el-button size="small" text type="primary" v-if="!scope.row.folderName"
  15. @click="view(scope.row)">详情
  16. </el-button>
  17. <el-button size="small" text type="warning" v-auth="'download'" @click="down(scope.row)">下载</el-button>
  18. <el-button size="small" text type="info" v-auth="'del'" @click="onRowDel(scope.row)">删除</el-button>
  19. </template>
  20. </el-table-column>
  21. </el-table>
  22. </el-card>
  23. <el-dialog header="123" v-model="dialogVisible" title="查看详情">
  24. <div v-for="line in topMsg" :key="line" class="error-line">{{ line }}</div>
  25. <div v-for="line in errorMessage" :key="line" class="error-line">{{ line }}</div>
  26. </el-dialog>
  27. </el-tab-pane>
  28. <!-- 数据库日志 -->
  29. <el-tab-pane label="数据库日志" name="2">
  30. <el-card shadow="hover">
  31. <el-table :data="tableData" style="width: 100%" row-key="id" v-loading="loading">
  32. <el-table-column prop="name" label="文件名" show-overflow-tooltip></el-table-column>
  33. <el-table-column prop="size" label="大小" show-overflow-tooltip></el-table-column>
  34. <el-table-column prop="changeAt" label="修改时间" min-width="100" align="center"></el-table-column>
  35. <el-table-column label="操作" width="200" align="center">
  36. <template #default="scope">
  37. <el-button size="small" text type="primary" v-if="!scope.row.folderName"
  38. @click="view(scope.row)">详情
  39. </el-button>
  40. <el-button size="small" text type="warning" v-auth="'download'" @click="down(scope.row)">下载</el-button>
  41. <el-button size="small" text type="info" v-auth="'del'" @click="onRowDel(scope.row)">删除</el-button>
  42. </template>
  43. </el-table-column>
  44. </el-table>
  45. </el-card>
  46. <el-dialog v-model="dialogVisible" title="查看详情">
  47. <div v-for="line in topMsg" :key="line" class="error-line">{{ line }}</div>
  48. <div v-for="line in errorMessage" :key="line" class="error-line">{{ line }}</div>
  49. </el-dialog>
  50. </el-tab-pane>
  51. <!-- 运行日志 -->
  52. <el-tab-pane label="运行日志" name="3" class="runMessage">
  53. <!-- 运行日志 -->
  54. <div v-for="line in topMsg" :key="line" class="error-line">{{ line }}</div>
  55. <div v-if="runButtonShow" v-loading="runLoading" v-for="line in runMessage" :key="line" class="error-line">{{ line }}</div>
  56. <div v-else class="error-line">暂无数据</div>
  57. <div class="run-button" v-if="runButtonShow">
  58. <el-button size="small" text type="warning" v-auth="'download'" @click="down">下载</el-button>
  59. <el-button size="small" text type="danger" v-auth="'del'" @click="onRowDel">删除</el-button>
  60. </div>
  61. </el-tab-pane>
  62. </el-tabs>
  63. </div>
  64. </div>
  65. </template>
  66. <script lang="ts" setup>
  67. import api from '/@/api/system';
  68. import {useSearch} from '/@/hooks/useCommon';
  69. import getOrigin from '/@/utils/origin'
  70. import downloadFile from '/@/utils/download';
  71. import {ref, getCurrentInstance, nextTick, onMounted} from 'vue';
  72. import {ElMessage, ElMessageBox} from "element-plus";
  73. const dialogVisible = ref(false);
  74. const errorMessage = ref([]);
  75. const runMessage = ref([]);
  76. const topMsg = ref([]);
  77. const activeName = '1';
  78. const types = ref('service');
  79. const runLoading = ref(false);
  80. const { proxy } = getCurrentInstance() as any;
  81. const isScrolling = ref(false);
  82. const chatContent: any = ref(null);
  83. const runButtonShow = ref(false);
  84. const runLogName = ref('');
  85. const {params, tableData, getList, loading} = useSearch<any[]>(api.lastLinesLog.getList, 'list', {types: types.value});
  86. getList();
  87. onMounted(() => {
  88. chatContent.value = document.getElementsByClassName('chatdoom')[0]
  89. scrollBottom()
  90. })
  91. const scrollBottom = () => {
  92. nextTick(() => {
  93. //注意要使用nextick以免获取不到dom
  94. if (!isScrolling.value) {
  95. // chatContent.value.scrollTop = chatContent.value.scrollHeight - chatContent.value.offsetHeight
  96. chatContent.value.scrollTop = chatContent.value.offsetHeight
  97. console.log(chatContent.value.scrollTop);
  98. }
  99. })
  100. }
  101. const view = (row: any) => {
  102. if (types.value == 'run') {
  103. row.name = row.value;
  104. }
  105. const es = new EventSource(getOrigin(import.meta.env.VITE_SERVER_URL + "/subscribe/logInfo?name=" + row.name + '&types=' + types.value));
  106. es.addEventListener('log', ({data}) => {
  107. topMsg.value.unshift(data);
  108. });
  109. api.lastLinesLog.detail({name: row.name, types: types.value}).then((res: any) => {
  110. if (types.value == 'run') {
  111. runMessage.value = res.list;
  112. runLoading.value = false;
  113. if (res.list.length > 0) {
  114. runButtonShow.value = true;
  115. }
  116. } else {
  117. errorMessage.value = res.list;
  118. dialogVisible.value = true;
  119. }
  120. });
  121. };
  122. const down = (row: any) => {
  123. if (types.value == 'run') {
  124. row.name = runLogName.value
  125. }
  126. api.lastLinesLog.down({name: row.name, types: types.value}).then((res: any) => downloadFile(res, types.value +"-"+ row.name))
  127. };
  128. const onRowDel = (row: any) => {
  129. if (types.value == 'run') {
  130. row.name = runLogName.value
  131. }
  132. let msg = '你确定要删除所选数据?';
  133. ElMessageBox.confirm(msg, '提示', {
  134. confirmButtonText: '确认',
  135. cancelButtonText: '取消',
  136. type: 'warning',
  137. }).then(() => {
  138. api.lastLinesLog.delete({name: row.name, types: types.value}).then(() => {
  139. params.types = types.value;
  140. if (types.value == 'run') {
  141. runButtonShow.value = false;
  142. } else {
  143. getList()
  144. }
  145. ElMessage.success('删除成功');
  146. });
  147. }).catch(() => { });
  148. };
  149. // 切换tab
  150. const handleClick = (tab: any, event: Event) => {
  151. if (tab.props.name == 1) {
  152. types.value = 'service';
  153. // 获取日志列表
  154. params.types = types.value;
  155. getList()
  156. } else if (tab.props.name == 2) {
  157. types.value = 'sql'
  158. params.types = types.value;
  159. getList();
  160. } else if (tab.props.name == 3) {
  161. runLoading.value = true;
  162. types.value = 'run'
  163. runLogName.value = 'sagoo-admin.log'
  164. params.type = types.value
  165. view(runLogName)
  166. }
  167. }
  168. </script>
  169. <style scoped>
  170. .run-button {
  171. position: fixed;
  172. bottom: 50px;
  173. right: 80px;
  174. }
  175. .runMessage {
  176. height: 500px;
  177. }
  178. .content-box {
  179. width: 100%;
  180. padding: 5px 20px 20px 20px;
  181. }
  182. .error-line {
  183. white-space: pre-line; /* 保留换行符 */
  184. }
  185. .el-tabs--card {
  186. height: calc(100vh - 110px);
  187. overflow-y: auto;
  188. }
  189. .el-tab-pane {
  190. position: relative;
  191. height: calc(100vh - 110px);
  192. overflow-y: auto;
  193. }
  194. </style>