This commit is contained in:
		
							
								
								
									
										18
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| .kpt-pipeline/ | ||||
| k8s/ | ||||
| skaffold.yaml | ||||
| README.md | ||||
| .git/ | ||||
| node_modules/ | ||||
| .drone.yml | ||||
|  | ||||
| # Editor directories and files | ||||
| .vscode/* | ||||
| !.vscode/extensions.json | ||||
| .idea | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
| *.kpt-pipeline | ||||
							
								
								
									
										2
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.drone.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| kind: template | ||||
| load: docker.yaml | ||||
							
								
								
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| # Logs | ||||
| logs | ||||
| *.log | ||||
| npm-debug.log* | ||||
| yarn-debug.log* | ||||
| yarn-error.log* | ||||
| pnpm-debug.log* | ||||
| lerna-debug.log* | ||||
|  | ||||
| node_modules | ||||
| .DS_Store | ||||
| dist | ||||
| dist-ssr | ||||
| coverage | ||||
| *.local | ||||
|  | ||||
| /cypress/videos/ | ||||
| /cypress/screenshots/ | ||||
|  | ||||
| # Editor directories and files | ||||
| .vscode/* | ||||
| !.vscode/extensions.json | ||||
| .idea | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
| *.kpt-pipeline | ||||
							
								
								
									
										22
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| FROM node AS dev | ||||
|  | ||||
| WORKDIR /app | ||||
| EXPOSE 8080 | ||||
|  | ||||
| COPY package* ./ | ||||
| RUN npm install | ||||
| COPY . . | ||||
|  | ||||
| ENTRYPOINT ["npm", "run", "dev"] | ||||
|  | ||||
| # builder | ||||
| FROM dev AS builder | ||||
| RUN npm run build | ||||
|  | ||||
| # serve | ||||
| FROM nginx AS prod | ||||
| WORKDIR /usr/share/nginx/html | ||||
| RUN rm -rf ./* | ||||
| COPY ./nginx.conf /etc/nginx/conf.d/default.conf | ||||
| COPY --from=builder /app/dist . | ||||
| ENTRYPOINT ["nginx", "-g", "daemon off;"] | ||||
							
								
								
									
										13
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <link rel="icon" href="/favicon.ico" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>Log Viewer</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="app"></div> | ||||
|     <script type="module" src="/src/main.js"></script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										58
									
								
								k8s/dev/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								k8s/dev/deployment.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| apiVersion: networking.k8s.io/v1 | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: playground | ||||
|   annotations: | ||||
|     kubernetes.io/ingress.class: traefik | ||||
|     cert-manager.io/cluster-issuer: default | ||||
|     traefik.ingress.kubernetes.io/router.entrypoints: websecure | ||||
|     traefik.ingress.kubernetes.io/router.middlewares: traefik-sso@kubernetescrd | ||||
|     traefik.ingress.kubernetes.io/router.tls: "true" | ||||
|     external-dns.alpha.kubernetes.io/target: traefik.k-space.ee | ||||
| spec: | ||||
|   rules: | ||||
|   - host: playground.k-space.ee | ||||
|     http: | ||||
|       paths: | ||||
|       - pathType: Prefix | ||||
|         path: "/" | ||||
|         backend: | ||||
|           service: | ||||
|             name: log-viewer-frontend | ||||
|             port: | ||||
|               number: 8080 | ||||
|   tls: | ||||
|   - hosts: | ||||
|     - playground.k-space.ee | ||||
|     secretName: playground-tls | ||||
| --- | ||||
| apiVersion: v1 | ||||
| kind: Service | ||||
| metadata: | ||||
|   name: log-viewer-frontend | ||||
| spec: | ||||
|   type: ClusterIP | ||||
|   selector: | ||||
|     app: log-viewer-frontend | ||||
|   ports: | ||||
|     - protocol: TCP | ||||
|       port: 8080 | ||||
| --- | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: log-viewer-frontend | ||||
| spec: | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: log-viewer-frontend | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: log-viewer-frontend | ||||
|     spec: | ||||
|       containers: | ||||
|       - name: log-viewer-frontend | ||||
|         image: harbor.k-space.ee/playground/log-viewer-frontend | ||||
|         ports: | ||||
|         - containerPort: 8080 | ||||
							
								
								
									
										9
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								nginx.conf
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| server { | ||||
|     listen       8080; | ||||
|     server_name  _; | ||||
|  | ||||
|     location / { | ||||
|         root   /usr/share/nginx/html; | ||||
|         index  index.html index.htm; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13612
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										13612
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										31
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| { | ||||
|   "name": "@k-space/log-viewer", | ||||
|   "version": "0.0.0", | ||||
|   "scripts": { | ||||
|     "serve": "vue-cli-service serve", | ||||
|     "build": "vite build", | ||||
|     "dev": "vite --port 8080 --host", | ||||
|     "preview": "vite preview --port 3003" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@ag-grid-community/core": "^28.2.0", | ||||
|     "@mdi/font": "5.9.55", | ||||
|     "@vue/cli-service": "^5.0.8", | ||||
|     "ag-grid-vue3": "^28.2.0", | ||||
|     "core-js": "^3.25.1", | ||||
|     "event-hooks-webpack-plugin": "^2.2.0", | ||||
|     "pinia": "^2.0.21", | ||||
|     "roboto-fontface": "*", | ||||
|     "single-spa-vue": "^2.5.1", | ||||
|     "systemjs-webpack-interop": "^2.3.7", | ||||
|     "vue": "^3.2.39", | ||||
|     "vuetify": "^3.0.0-beta.0", | ||||
|     "webfontloader": "^1.0.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@vitejs/plugin-vue": "^3.0.3", | ||||
|     "vite": "^3.0.9", | ||||
|     "vue-cli-plugin-vuetify": "~2.5.8", | ||||
|     "webpack-plugin-vuetify": "^2.0.0-alpha.0" | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								public/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.2 KiB | 
							
								
								
									
										0
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								public/index.html
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										42
									
								
								skaffold.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								skaffold.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| apiVersion: skaffold/v3alpha1 | ||||
| kind: Config | ||||
| metadata: | ||||
|   name: log-viewer-backend | ||||
|  | ||||
| build: | ||||
|   artifacts: | ||||
|   - image: harbor.k-space.ee/playground/log-viewer-frontend | ||||
|     docker: | ||||
|       dockerfile: Dockerfile | ||||
|  | ||||
| deploy: | ||||
|   kubectl: {} | ||||
|  | ||||
| manifests: | ||||
|   rawYaml: | ||||
|      - k8s/staging/deployment.yaml | ||||
|  | ||||
| profiles: | ||||
|   - name: dev | ||||
|     activation: | ||||
|       - command: dev | ||||
|     build: | ||||
|       artifacts: | ||||
|         - image: harbor.k-space.ee/playground/log-viewer-frontend | ||||
|           docker: | ||||
|             target: dev | ||||
|           sync: | ||||
|             manual: | ||||
|               - src: 'src/**/*.vue' | ||||
|                 dest: . | ||||
|               - src: 'src/**/*.js' | ||||
|                 dest: . | ||||
|               - src: 'src/**/*.css' | ||||
|                 dest: . | ||||
|               - src: 'src/**/*.svg' | ||||
|                 dest: . | ||||
|               - src: 'index.html' | ||||
|                 dest: . | ||||
|     manifests: | ||||
|       rawYaml: | ||||
|         - k8s/dev/deployment.yaml | ||||
							
								
								
									
										15
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/App.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| <script setup> | ||||
| import LogViewer from './components/LogViewer.vue' | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|     <LogViewer /> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: 'app1', | ||||
| } | ||||
| </script> | ||||
| <style scoped> | ||||
| </style> | ||||
							
								
								
									
										1
									
								
								src/assets/logo.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/assets/logo.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"  xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg> | ||||
| After Width: | Height: | Size: 308 B | 
							
								
								
									
										15
									
								
								src/assets/main.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/assets/main.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
|  | ||||
| div#app { | ||||
|   width: 100%; | ||||
|   height: 100vh; | ||||
| } | ||||
|  | ||||
| .screenshots-drawer { | ||||
|   position: fixed; | ||||
|   display: flex; | ||||
|   flex-direction: column; | ||||
| } | ||||
|  | ||||
| .ag-theme-material { | ||||
|   --ag-value-change-value-highlight-background-color: #f9ff99; | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/components/ComboboxFilter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/components/ComboboxFilter.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| export default { | ||||
|     template: ` | ||||
|       <select v-model="filter" class="v-select"> | ||||
|         <option value=""> </option> | ||||
|         <option v-for="option in params.options" :value="option"> | ||||
|            {{ option }} | ||||
|         </option> | ||||
|       </select> | ||||
|     `, | ||||
|     data: function () { | ||||
|         return { | ||||
|             filter: '', | ||||
|         }; | ||||
|     }, | ||||
|     methods: { | ||||
|         updateFilter() { | ||||
|             this.params.filterChangedCallback(); | ||||
|         }, | ||||
|  | ||||
|         doesFilterPass(params) { | ||||
|             const value = this.params.field.split('.').reduce((a, b) => a[b], params.data); | ||||
|             return value === this.filter; | ||||
|         }, | ||||
|  | ||||
|         isFilterActive() { | ||||
|             return this.filter !== '' | ||||
|         }, | ||||
|     }, | ||||
| }; | ||||
							
								
								
									
										83
									
								
								src/components/ExamineLogModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/components/ExamineLogModal.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| <template> | ||||
|   <v-dialog | ||||
|       v-model="examineLog" | ||||
|       width="50wv" | ||||
|   > | ||||
|     <v-card> | ||||
|       <v-card-text style="height: 70vh"> | ||||
|         <ag-grid-vue | ||||
|             style="width: 100%; height: 100%;" | ||||
|             class="ag-theme-material" | ||||
|             @grid-ready="onGridReady" | ||||
|             :columnDefs="columnDefs" | ||||
|             :row-data="examineLogContent" | ||||
|             :supress-horisontal-scroll="true" | ||||
|             :enable-scrolling="true" | ||||
|             :enableCellTextSelection="true" | ||||
|             :ensureDomOrder="true" | ||||
|         ></ag-grid-vue> | ||||
|       </v-card-text> | ||||
|       <v-card-actions> | ||||
|         <v-btn color="primary" block @click="closeModal">Close</v-btn> | ||||
|       </v-card-actions> | ||||
|     </v-card> | ||||
|   </v-dialog> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { AgGridVue } from "ag-grid-vue3"; | ||||
| import "ag-grid-community/styles//ag-grid.css"; | ||||
| import "ag-grid-community/styles//ag-theme-material.css"; | ||||
| import ScreenshotCell from "./ScreenshotCell.js"; | ||||
| import { VCard, VCardText, VCardActions } from 'vuetify/components/VCard' | ||||
| import { VDialog } from 'vuetify/components/VDialog' | ||||
| import { VBtn } from 'vuetify/components/VBtn' | ||||
| import { VTable } from 'vuetify/components/VTable' | ||||
|  | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
|     AgGridVue, | ||||
|     VCard, | ||||
|     VCardText, | ||||
|     VCardActions, | ||||
|     VBtn, | ||||
|     VDialog, | ||||
|     VTable, | ||||
|     ScreenshotCell: ScreenshotCell | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       columnDefs:  [ | ||||
|         { | ||||
|           field: 'key', | ||||
|           sortable: true, | ||||
|           filter: 'agTextColumnFilter', | ||||
|           resizable: true | ||||
|         }, | ||||
|         { | ||||
|           field: 'value', | ||||
|           sortable: true, | ||||
|           filter: 'agTextColumnFilter', | ||||
|           resizable: true | ||||
|         }, | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     examineLogContent: Array, | ||||
|     closeModal: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     examineLog() { | ||||
|       return !!this.examineLogContent | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     onGridReady(params) { | ||||
|       params.api.sizeColumnsToFit() | ||||
|     }, | ||||
|   } | ||||
| } | ||||
|  | ||||
| </script> | ||||
							
								
								
									
										173
									
								
								src/components/LogViewer.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/components/LogViewer.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,173 @@ | ||||
| <template> | ||||
|   <div style="height: 100%; width: 100%"> | ||||
|   <ag-grid-vue | ||||
|       style="width: 100%; height: 100%;" | ||||
|       class="ag-theme-material" | ||||
|       @grid-ready="onGridReady" | ||||
|       :defaultColDef="defaultColDef" | ||||
|       :columnDefs="columnDefs" | ||||
|       :pagination="true" | ||||
|       :paginationAutoPageSize=true | ||||
|       :row-data="null" | ||||
|       row-selection="single" | ||||
|       :onRowSelected="openExamineLog" | ||||
|       :supress-horisontal-scroll="true" | ||||
|       :enable-scrolling="true" | ||||
|     ></ag-grid-vue> | ||||
|     <ExamineLogModal :examine-log-content="examineLogContent" :close-modal="closeExamineLog" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { AgGridVue } from "ag-grid-vue3"; | ||||
| import "ag-grid-community/styles//ag-grid.css"; | ||||
| import "ag-grid-community/styles//ag-theme-material.css"; | ||||
| import ScreenshotCell from "./ScreenshotCell.js"; | ||||
| import ExamineLogModal from "./ExamineLogModal.vue"; | ||||
| import ComboboxFilter from "./ComboboxFilter.js"; | ||||
|  | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
|     ExamineLogModal, | ||||
|     AgGridVue, | ||||
|     ComboboxFilter, | ||||
|     ScreenshotCell: ScreenshotCell | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       examineLogContent: null, | ||||
|       gridApi: null, | ||||
|       gridColumnApi: null, | ||||
|       defaultColDef: { | ||||
|         width: 50, | ||||
|         initialPinned: true, | ||||
|         resizable: true, | ||||
|         enableCellChangeFlash: true | ||||
|       }, | ||||
|       currentRowCount: 0, | ||||
|       comboBoxOptions: {}, | ||||
|       viewRowCount: 20, | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     columnDefs() { | ||||
|       return [ | ||||
|         { | ||||
|           field: '@timestamp', | ||||
|           width: 70, | ||||
|           sortable: true | ||||
|         }, | ||||
|         { | ||||
|           field: 'kubernetes.namespace', | ||||
|           headerName: 'namespace', | ||||
|           tooltipValueGetter: (params) => params.value, | ||||
|           filter: ComboboxFilter, | ||||
|           filterParams: { | ||||
|             options: this.comboBoxOptions['kubernetes.namespace'], | ||||
|             field: 'kubernetes.namespace', | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           field: 'kubernetes.pod.name', | ||||
|           headerName: 'pod', | ||||
|           tooltipValueGetter: (params) => params.value, | ||||
|           filter: ComboboxFilter, | ||||
|           filterParams: { | ||||
|             options: this.comboBoxOptions['kubernetes.pod.name'], | ||||
|             field: 'kubernetes.pod.name', | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           field: 'kubernetes.container.name', | ||||
|           headerName: 'container', | ||||
|           tooltipValueGetter: (params) => params.value, | ||||
|           filter: ComboboxFilter, | ||||
|           filterParams: { | ||||
|             options: this.comboBoxOptions['kubernetes.container.name'], | ||||
|             field: 'kubernetes.container.name', | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           field: 'message', | ||||
|           tooltipValueGetter: (params) => params.value, | ||||
|           width: 500, | ||||
|         }, | ||||
|         { | ||||
|           field: 'stream', | ||||
|         }, | ||||
|       ]; | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.setupStream() | ||||
|   }, | ||||
|   methods: { | ||||
|     setupStream() { | ||||
|       let es = new EventSource('/events'); | ||||
|       es.onmessage = (e) => this.handleReceiveMessage(e) | ||||
|       es.addEventListener("filters", (e) => this.handleReceiveFilters(e)) | ||||
|     }, | ||||
|     onGridReady(params) { | ||||
|       this.gridApi = params.api; | ||||
|       this.gridColumnApi = params.columnApi; | ||||
|     }, | ||||
|     handleReceiveMessage (event) { | ||||
|       const eventData = this.parseEventData(event.data); | ||||
|       const res = this.gridApi.applyTransaction({ | ||||
|         add: [eventData] | ||||
|       }); | ||||
|       const rowNode = res.add[0] | ||||
|       this.gridApi.flashCells({ rowNodes: [rowNode]}); | ||||
|       this.gridApi.sizeColumnsToFit() | ||||
|     }, | ||||
|     handleReceiveFilters (event) { | ||||
|       this.comboBoxOptions = this.parseEventData(event.data); | ||||
|     }, | ||||
|     parseEventData (eventData) { | ||||
|       try { | ||||
|         let json = JSON.parse(eventData) | ||||
|         if (!json.message) { | ||||
|           json.message = JSON.stringify(json.json) | ||||
|         } | ||||
|         return json | ||||
|       } catch (e) { | ||||
|         console.error(e, eventData) | ||||
|       } | ||||
|     }, | ||||
|     openExamineLog (row) { | ||||
|       const selectedRow = row.data | ||||
|       row.node.setSelected(false) | ||||
|       this.examineLog = true | ||||
|       const flattened = flattenObj(selectedRow) | ||||
|       const pairs = []; | ||||
|       Object.keys(flattened).map((key) => { | ||||
|         pairs.push({ | ||||
|           key: key, | ||||
|           value: flattened[key] | ||||
|         }) | ||||
|       }) | ||||
|       this.examineLogContent = pairs | ||||
|     }, | ||||
|     closeExamineLog () { | ||||
|       this.examineLogContent = null | ||||
|     } | ||||
|   }, | ||||
| } | ||||
|  | ||||
| const flattenObj = (ob) => { | ||||
|   let result = {}; | ||||
|   for (const i in ob) { | ||||
|     if ((typeof ob[i]) === 'object' && !Array.isArray(ob[i])) { | ||||
|       const temp = flattenObj(ob[i]); | ||||
|       for (const j in temp) { | ||||
|         result[i + '.' + j] = temp[j]; | ||||
|       } | ||||
|     } | ||||
|     else { | ||||
|       result[i] = ob[i]; | ||||
|     } | ||||
|   } | ||||
|   return result; | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										30
									
								
								src/components/ScreenshotCell.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/components/ScreenshotCell.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| export default { | ||||
|     template: `<div> | ||||
|       <a @click="openDrawer">View screenshots</a> | ||||
|       <div v-if="drawerOpen" class="screenshots-drawer"> | ||||
|         <img v-for="screenshot in screenshots" :src="screenshot.orig"/> | ||||
|       </div> | ||||
|     </div>`, | ||||
|     data: function () { | ||||
|         return { | ||||
|             screenshots: [], | ||||
|             drawerOpen: false, | ||||
|         }; | ||||
|     }, | ||||
|     beforeMount() { | ||||
|         this.updateImage(this.params); | ||||
|         this.updateImage(this.params); | ||||
|     }, | ||||
|     methods: { | ||||
|         updateImage(params) { | ||||
|             this.screenshots = params.value | ||||
|             this.value = params.value; | ||||
|         }, | ||||
|         refresh(params) { | ||||
|             this.updateImage(params); | ||||
|         }, | ||||
|         openDrawer () { | ||||
|             this.drawerOpen = true | ||||
|         } | ||||
|     }, | ||||
| }; | ||||
							
								
								
									
										10
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/main.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import { createApp } from 'vue' | ||||
| import App from './App.vue' | ||||
| import vuetify from './plugins/vuetify' | ||||
| import { loadFonts } from './plugins/webfontloader' | ||||
| import './assets/main.css' | ||||
| loadFonts() | ||||
|  | ||||
| createApp(App) | ||||
|   .use(vuetify) | ||||
|   .mount('#app') | ||||
							
								
								
									
										10
									
								
								src/plugins/vuetify.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/plugins/vuetify.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| // Styles | ||||
| import '@mdi/font/css/materialdesignicons.css' | ||||
| import 'vuetify/styles' | ||||
|  | ||||
| // Vuetify | ||||
| import { createVuetify } from 'vuetify' | ||||
|  | ||||
| export default createVuetify( | ||||
|   // https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides | ||||
| ) | ||||
							
								
								
									
										15
									
								
								src/plugins/webfontloader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/plugins/webfontloader.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| /** | ||||
|  * plugins/webfontloader.js | ||||
|  * | ||||
|  * webfontloader documentation: https://github.com/typekit/webfontloader | ||||
|  */ | ||||
|  | ||||
| export async function loadFonts () { | ||||
|   const webFontLoader = await import(/* webpackChunkName: "webfontloader" */'webfontloader') | ||||
|  | ||||
|   webFontLoader.load({ | ||||
|     google: { | ||||
|       families: ['Roboto:100,300,400,500,700,900&display=swap'], | ||||
|     }, | ||||
|   }) | ||||
| } | ||||
							
								
								
									
										3
									
								
								src/set-public-path.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/set-public-path.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| import { setPublicPath } from "systemjs-webpack-interop"; | ||||
|  | ||||
| setPublicPath("app1"); | ||||
							
								
								
									
										14
									
								
								src/stores/counter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/stores/counter.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| import { ref, computed } from 'vue' | ||||
| import { defineStore } from 'pinia' | ||||
|  | ||||
| export const useCounterStore = defineStore('counter', () => { | ||||
|   const count = ref(0) | ||||
|   const doubleCount = computed(() => count.value * 2) | ||||
|   function increment() { | ||||
|     count.value++ | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
|   return { count, doubleCount, increment } | ||||
| }) | ||||
							
								
								
									
										26
									
								
								vite.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vite.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| import vue from '@vitejs/plugin-vue' | ||||
|  | ||||
| export default { | ||||
|   resolve: { | ||||
|     alias: { | ||||
|       vue: 'vue/dist/vue.esm-bundler.js' | ||||
|     } | ||||
|   }, | ||||
|   rollupOptions: { | ||||
|     input: 'src/main.js', | ||||
|     format: 'system', | ||||
|     preserveEntrySignatures: true | ||||
|   }, | ||||
|   plugins: [vue({ | ||||
|     template: { | ||||
|       transformAssetUrls: { | ||||
|         base: '/src' | ||||
|       } | ||||
|     } | ||||
|   })], | ||||
|   pluginOptions: { | ||||
|     vuetify: { | ||||
|       // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										43
									
								
								vue.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								vue.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
|  | ||||
| const path = require('path'); | ||||
| const fs = require('fs'); | ||||
| const EventHooksPlugin = require('event-hooks-webpack-plugin'); | ||||
|  | ||||
| module.exports = { | ||||
|     publicPath: '/logs', | ||||
|     chainWebpack: (config) => { | ||||
|         config.devServer.headers({ | ||||
|             'Access-Control-Allow-Origin': '*', | ||||
|         }); | ||||
|         config.devServer.set('port', 8080); | ||||
|         config.devServer.set('hot', true); | ||||
|  | ||||
|         config.output.filename('[name].js'); | ||||
|         config.output.publicPath('/logs'); | ||||
|  | ||||
|         config.externals([ | ||||
|             'vue', | ||||
|             'vue-router' | ||||
|         ]); | ||||
|     }, | ||||
|     lintOnSave: true, | ||||
|     filenameHashing: false, | ||||
|     configureWebpack: { | ||||
|         plugins: [ | ||||
|             new EventHooksPlugin({ | ||||
|                 done: () => { | ||||
|                     if (process.env.NODE_ENV !== 'development') { | ||||
|                         const buildDir = path.join(__dirname, '/dist'); | ||||
|                         fs.unlinkSync(`${buildDir}/index.html`); | ||||
|                     } | ||||
|                 }, | ||||
|             }), | ||||
|         ], | ||||
|     }, | ||||
|  | ||||
|     pluginOptions: { | ||||
|       vuetify: { | ||||
| 			// https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vuetify-loader | ||||
| 		} | ||||
|     } | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user