first commit
This commit is contained in:
commit
93953e52d7
14
.eslintrc.cjs
Normal file
14
.eslintrc.cjs
Normal file
@ -0,0 +1,14 @@
|
||||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
||||
|
||||
module.exports = {
|
||||
"root": true,
|
||||
"extends": [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-prettier"
|
||||
],
|
||||
"env": {
|
||||
"vue/setup-compiler-macros": true
|
||||
}
|
||||
}
|
27
.gitignore
vendored
Normal file
27
.gitignore
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
# 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
|
||||
config.js
|
||||
config.prod.js
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@ -0,0 +1,29 @@
|
||||
FROM debian:bullseye-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND="noninteractive"
|
||||
|
||||
RUN apt-get update && apt-get -y upgrade \
|
||||
&& apt-get -y install \
|
||||
curl nodejs npm \
|
||||
&& npm install -g vue vite n \
|
||||
&& n stable \
|
||||
&& mkdir /app \
|
||||
&& rm -rf /var/lib/apt/lists/* /usr/share/man/* /usr/share/doc/*
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY ./public public
|
||||
COPY ./src src
|
||||
COPY ./config.prod.js ./config.js
|
||||
COPY ./index.html .
|
||||
COPY ./package.json .
|
||||
COPY ./package-lock.json .
|
||||
COPY ./postcss.config.js .
|
||||
COPY ./tailwind.config.js .
|
||||
COPY ./vite.config.js .
|
||||
|
||||
RUN npm install && npm run build
|
||||
|
||||
FROM nginx
|
||||
COPY --from=0 /app/dist /usr/share/nginx/html
|
||||
COPY ./nginx/conf /etc/nginx/conf.d/
|
6
config.js.example
Normal file
6
config.js.example
Normal file
@ -0,0 +1,6 @@
|
||||
const config = {
|
||||
APP_URL: 'http://localhost:3000',
|
||||
// API_URL: 'http://localhost:8080/v1',
|
||||
};
|
||||
|
||||
export { config }
|
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>Tools</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/app.js"></script>
|
||||
</body>
|
||||
</html>
|
23
nginx/conf/default.conf
Normal file
23
nginx/conf/default.conf
Normal file
@ -0,0 +1,23 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
#access_log /var/log/nginx/host.access.log main;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
location ~ /map/\d+$ {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# redirect server error pages to the static page /50x.html
|
||||
#
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
1768
package-lock.json
generated
Normal file
1768
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
package.json
Normal file
33
package.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "yourmap-tracker",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"lint": "eslint .. --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"dev": "vite",
|
||||
"preview": "vite preview --port 3000"
|
||||
},
|
||||
"dependencies": {
|
||||
"autoprefixer": "^10",
|
||||
"axios": "^0.27.2",
|
||||
"mitt": "^3.0.0",
|
||||
"pinia": "^2.0.14",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3",
|
||||
"vue": "^3.2.36",
|
||||
"vue-axios": "^3.4.1",
|
||||
"vue-router": "^4.0.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.1.0",
|
||||
"@vitejs/plugin-vue": "^2.3.3",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"eslint": "^8.5.0",
|
||||
"eslint-plugin-vue": "^8.2.0",
|
||||
"prettier": "^2.5.1",
|
||||
"sass": "^1.52.3",
|
||||
"vite": "^2.9.9",
|
||||
"vue-cli-plugin-tailwind": "~3.0.0"
|
||||
}
|
||||
}
|
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
71
src/App.vue
Normal file
71
src/App.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div
|
||||
class="w-full flex flex-col sm:flex-row flex-grow"
|
||||
:class="{ 'sidebar-toggle': sidebarToggle }">
|
||||
<aside
|
||||
class="hidden md:block h-screen overflow-y-auto no-scrollbar border-r">
|
||||
<Sidebar/>
|
||||
</aside>
|
||||
|
||||
<main role="main" class="w-full h-full flex-grow overflow-auto p-3">
|
||||
<RouterView/>
|
||||
</main>
|
||||
|
||||
<button class="sidebar-toggle-btn md:hidden" v-on:click="toggleSidebar">
|
||||
<svg class="svg-icon" style="width: 30px; height: 30px;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M682.666667 682.666667h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666H170.666667v-170.666666z m512-256h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666h-170.666666v-170.666666z m-256 0h170.666666v170.666666H170.666667v-170.666666z m512-256h170.666666v170.666666h-170.666666V170.666667z m-256 0h170.666666v170.666666h-170.666666V170.666667zM170.666667 170.666667h170.666666v170.666666H170.666667V170.666667z" fill="#000000" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { RouterView } from 'vue-router'
|
||||
import Sidebar from '@/components/Sidebar.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Sidebar,
|
||||
RouterView,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
sidebarToggle: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleSidebar() {
|
||||
this.sidebarToggle = !this.sidebarToggle;
|
||||
|
||||
this.emitter.emit('sidebar.toggle');
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
aside {
|
||||
width: 480px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.sidebar-toggle {
|
||||
aside {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: initial;
|
||||
}
|
||||
|
||||
main {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&-btn {
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
background-color: white;
|
||||
border-radius: 5px;
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
23
src/app.js
Normal file
23
src/app.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import axios from 'axios'
|
||||
import VueAxios from 'vue-axios'
|
||||
import mitt from 'mitt';
|
||||
|
||||
import './assets/app.scss'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
axios.defaults.withCredentials = true;
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.config.globalProperties.emitter = mitt();
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(VueAxios, axios)
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
27
src/assets/app.scss
Normal file
27
src/assets/app.scss
Normal file
@ -0,0 +1,27 @@
|
||||
@import "tailwindcss/base";
|
||||
@import "tailwindcss/components";
|
||||
@import "tailwindcss/utilities";
|
||||
|
||||
|
||||
/**
|
||||
* Components
|
||||
*/
|
||||
input[type=text], input[type=number], textarea {
|
||||
@apply appearance-none border leading-tight focus:outline-none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tools
|
||||
*/
|
||||
.tool-title {
|
||||
@apply font-bold;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
@apply flex flex-col mb-3;
|
||||
}
|
||||
|
||||
.input-group > label {
|
||||
font-style: italic;
|
||||
}
|
31
src/components/Sidebar.vue
Normal file
31
src/components/Sidebar.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<header class="flex">
|
||||
<span class="my-auto ml-2 font-bold">Tools</span>
|
||||
</header>
|
||||
|
||||
<hr class="mt-2 mb-2">
|
||||
|
||||
<nav class="flex flex-col ml-2">
|
||||
<router-link v-for="[key, value] of Object.entries(menuRoutes)" :to="{name:key}" tag="button">{{ value }}</router-link>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Sidebar",
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
menuRoutes: {
|
||||
'home': 'Home',
|
||||
'sql_split_in': 'SQL split IN',
|
||||
'str_to_upper': 'Str to upper',
|
||||
'str_to_lower': 'Str to lower',
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
40
src/router/index.js
Normal file
40
src/router/index.js
Normal file
@ -0,0 +1,40 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
/**
|
||||
* General
|
||||
*/
|
||||
{
|
||||
path: '/',
|
||||
name: 'home',
|
||||
component: () => import('../views/HomeView.vue'),
|
||||
},
|
||||
|
||||
/**
|
||||
* String manipulation
|
||||
*/
|
||||
{
|
||||
path: '/str_to_upper',
|
||||
name: 'str_to_upper',
|
||||
component: () => import('../views/StrToUpper.vue'),
|
||||
},
|
||||
{
|
||||
path: '/str_to_lower',
|
||||
name: 'str_to_lower',
|
||||
component: () => import('../views/StrToLower.vue'),
|
||||
},
|
||||
|
||||
/**
|
||||
* SQL manipulation
|
||||
*/
|
||||
{
|
||||
path: '/sql_split_in',
|
||||
name: 'sql_split_in',
|
||||
component: () => import('../views/SQLSplitInView.vue'),
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export default router
|
0
src/stores/.gitkeep
Normal file
0
src/stores/.gitkeep
Normal file
7
src/utils/unproxy.js
Normal file
7
src/utils/unproxy.js
Normal file
@ -0,0 +1,7 @@
|
||||
function unproxy(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export {
|
||||
unproxy,
|
||||
}
|
36
src/utils/useDebouncedRef.js
Normal file
36
src/utils/useDebouncedRef.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { ref, customRef } from 'vue'
|
||||
|
||||
const debounce = (fn, delay = 0, immediate = false) => {
|
||||
let timeout
|
||||
return (...args) => {
|
||||
if (immediate && !timeout) fn(...args)
|
||||
clearTimeout(timeout)
|
||||
|
||||
timeout = setTimeout(() => {
|
||||
fn(...args)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
const useDebouncedRef = (initialValue, delay, immediate) => {
|
||||
const state = ref(initialValue)
|
||||
const debouncedRef = customRef((track, trigger) => ({
|
||||
get() {
|
||||
track()
|
||||
return state.value
|
||||
},
|
||||
set: debounce(
|
||||
value => {
|
||||
state.value = value
|
||||
trigger()
|
||||
},
|
||||
delay,
|
||||
immediate
|
||||
),
|
||||
}))
|
||||
return debouncedRef
|
||||
}
|
||||
|
||||
export {
|
||||
useDebouncedRef,
|
||||
}
|
7
src/views/HomeView.vue
Normal file
7
src/views/HomeView.vue
Normal file
@ -0,0 +1,7 @@
|
||||
<template>
|
||||
/home
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
103
src/views/SQLSplitInView.vue
Normal file
103
src/views/SQLSplitInView.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<h2 class="tool-title">SQL split IN</h2>
|
||||
<hr class="mt-5 mb-5">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="field_name">Field name</label>
|
||||
<input id="field_name" v-model="toolData.fieldName" placeholder="D.ID" type="text">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="data">Data</label>
|
||||
<textarea id="data" v-model="toolData.data" style="height: 150px"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="data_delimiter">Delimiter</label>
|
||||
<input id="data_delimiter" v-model="toolData.dataDelimiter" placeholder="," type="text">
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label>Wrap in quotes</label>
|
||||
|
||||
<div>
|
||||
<input id="wrap_in_quotes_no" value="" name="wrap_in_quotes" v-model="toolData.wrapInQuotes" type="radio"> <label for="wrap_in_quotes_no">No</label><br>
|
||||
<input id="wrap_in_quotes_single" value="single" name="wrap_in_quotes" v-model="toolData.wrapInQuotes" type="radio"> <label for="wrap_in_quotes_single">Single</label><br>
|
||||
<input id="wrap_in_quotes_double" value="double" name="wrap_in_quotes" v-model="toolData.wrapInQuotes" type="radio"> <label for="wrap_in_quotes_double">Double</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="chunk_by">Chunk by</label>
|
||||
<input id="chunk_by" v-model="toolData.chunkBy" placeholder="900" type="number">
|
||||
</div>
|
||||
|
||||
<hr class="mt-5 mb-5">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="result">Result</label>
|
||||
<textarea id="result" v-model="toolResult" style="height: 150px"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
function arrayChunk(arr, chunkSize) {
|
||||
const res = [];
|
||||
while (arr.length > 0) {
|
||||
const chunk = arr.splice(0, chunkSize);
|
||||
res.push(chunk);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
toolData: {
|
||||
fieldName: '',
|
||||
data: '',
|
||||
dataDelimiter: '',
|
||||
wrapInQuotes: '',
|
||||
chunkBy: 900,
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toolResult() {
|
||||
let data = this.toolData.data
|
||||
.split(this.toolData.dataDelimiter ? this.toolData.dataDelimiter : '\n');
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let value = data[i];
|
||||
|
||||
switch (this.toolData.wrapInQuotes) {
|
||||
case 'single':
|
||||
value = "'" + value.replace("'", "\\'") + "'";
|
||||
break;
|
||||
case 'double':
|
||||
value = '"' + value.replace('"', '\\"') + '"';
|
||||
break;
|
||||
}
|
||||
|
||||
data[i] = value;
|
||||
}
|
||||
|
||||
data = arrayChunk(data, this.toolData.chunkBy);
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let chunk = data[i];
|
||||
|
||||
|
||||
|
||||
data[i] = '(' + chunk.join(',') + ')';
|
||||
}
|
||||
|
||||
return '(' + this.toolData.fieldName + ' IN ' + data.join(' OR ' + this.toolData.fieldName + ' IN ') + ')';
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
37
src/views/StrToLower.vue
Normal file
37
src/views/StrToLower.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<h2 class="tool-title">Str to lower</h2>
|
||||
<hr class="mt-5 mb-5">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="data">Data</label>
|
||||
<textarea id="data" v-model="toolData.data" style="height: 150px"></textarea>
|
||||
</div>
|
||||
|
||||
<hr class="mt-5 mb-5">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="result">Result</label>
|
||||
<textarea id="result" v-model="toolResult" style="height: 150px"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
toolData: {
|
||||
data: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toolResult() {
|
||||
return this.toolData.data.toLocaleLowerCase();
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
37
src/views/StrToUpper.vue
Normal file
37
src/views/StrToUpper.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<h2 class="tool-title">Str to upper</h2>
|
||||
<hr class="mt-5 mb-5">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="data">Data</label>
|
||||
<textarea id="data" v-model="toolData.data" style="height: 150px"></textarea>
|
||||
</div>
|
||||
|
||||
<hr class="mt-5 mb-5">
|
||||
|
||||
<div class="input-group">
|
||||
<label for="result">Result</label>
|
||||
<textarea id="result" v-model="toolResult" style="height: 150px"></textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
toolData: {
|
||||
data: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
toolResult() {
|
||||
return this.toolData.data.toLocaleUpperCase();
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
17
tailwind.config.js
Normal file
17
tailwind.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
module.exports = {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
// "./node_modules/flowbite/**/*.js",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
zIndex: {
|
||||
'500': '500',
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
// require('flowbite/plugin')
|
||||
],
|
||||
}
|
29
vite.config.js
Normal file
29
vite.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
import {fileURLToPath, URL} from 'url'
|
||||
|
||||
import {defineConfig} from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
|
||||
function pathTo(path) {
|
||||
return fileURLToPath(new URL(path, import.meta.url));
|
||||
}
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': pathTo('./src', import.meta.url),
|
||||
'@node_modules': pathTo('./node_modules'),
|
||||
}
|
||||
},
|
||||
server: {
|
||||
host: true,
|
||||
port: 3000,
|
||||
cors: false,
|
||||
},
|
||||
preview: {
|
||||
host: true,
|
||||
port: 3000,
|
||||
cors: false,
|
||||
}
|
||||
})
|
Loading…
x
Reference in New Issue
Block a user