path.ts
const env: any = process.env.NODE_ENV
export = {
baseUrl: env === 'development' ? '/test' : '/api',
proxy: '/api'
}
array.ts
export const parseToValueConfigArr = (obj: { [propName: string]: any }) => {
return Object.keys(obj).map((k) => {
return {
name: obj[k],
value: k
}
})
}
data_type.ts
const getType = (input: any) => {
const type: string = Object.prototype.toString.call(input)
return type.slice(8, type.length - 1)
}
export const isBoolean = (input: any): input is boolean => getType(input) === 'Boolean'
export const isNumber = (input: any): input is number => !isNaN(input) && getType(input) === 'Number'
export const isObject = (input: any): input is ({ [propName: string]: any }) => getType(input) === 'Object'
export const isString = (input: any): input is string => getType(input) === 'String'
export const isUndefined = (input: any): input is undefined => getType(input) === 'Undefined'
export const isArray = (input: any): input is any[] => getType(input) === 'Array'
export const getUid = () => Date.now() + Math.random().toString(16).slice(2)
clearInvalidParams.ts
export const c = (params: any) => {
const newParams: any = {}
Object.keys(params).forEach((key: any) => {
if (typeof params[key] === 'number') {
newParams[key] = params[key]
}
else if (typeof params[key] === 'boolean') {
newParams[key] = params[key]
}
else if (params[key]) {
newParams[key] = params[key]
}
})
return newParams
}
copy.ts
export const copyToClip = (content: string) => {
const aux = document.createElement('input');
aux.setAttribute('value', content);
document.body.appendChild(aux);
aux.select();
document.execCommand('copy');
document.body.removeChild(aux);
alert('复制成功')
}
export const execCommand = (command: string, value: string) => {
const input = document.createElement('input')
input.setAttribute('value', value)
input.setAttribute('id', 'copy-container')
document.body.appendChild(input)
input.select()
const html = document.getElementById('copy-container')
if (document.execCommand(command)) {
document.execCommand(command)
html && document.body.removeChild(html)
return true
}
html && document.body.removeChild(html)
return false
}
export function getRect(dom: HTMLElement) {
const rect = dom.getBoundingClientRect()
let { left, top, bottom, right, width, height } = rect
return { left, top, bottom, right, offsetHeight: width, offsetWidth: width }
}
date.ts
import moment, { Moment } from 'moment'
export function validateTime(time: string | number) {
const invalidStr = '1971-01-01 00:00:00'
const invalidNum = 31507200000
return time !== invalidStr && time !== invalidNum && time !== '-' && moment(time).isValid()
}
export function adjustTime(timeStr: string, timeZone: number, showUTC?: boolean) {
if (!validateTime(timeStr) || timeZone == 0 || !timeZone || timeZone == 480) {
return timeStr
}
let beijingTimestamp = moment(timeStr).valueOf()
let utcTimestamp = beijingTimestamp - 8 * 60 * 60 * 1000
let targetTimestamp = utcTimestamp + timeZone * 60 * 1000
return `${formatTime(moment(targetTimestamp))}${showUTC ? `(UTC${timeZone / 60})` : ''}`
}
export function formatTime(date: Moment, formatSty: string = 'YYYY-MM-DD HH:mm:ss') {
return date.format(formatSty)
}
export function getDistanceStr(distance: number) {
return distance >= 1000 ? `${(distance / 1000).toFixed(1)}km` : `${distance}m`
}
export function getEtaStr(eta: number) {
return eta >= 60 ? `${(eta / 60).toFixed(0)}min` : `${eta}s`
}
decoration.ts
export function throttle(wait: number, fn: any) {
let previous = 0
let timer!: any
return async function() {
const context = this
const args = arguments
if (!previous) {
previous = Date.now()
fn.apply(context, args)
return
}
if (Date.now() - previous < wait) {
if (timer) {
clearTimeout(timer)
} else {
timer = setTimeout(() => {
previous = Date.now()
fn.apply(context, args)
})
}
return
}
previous = Date.now()
fn.apply(context, args)
}
}
export function debounce(wait: number, fn: any) {
let timer: any = null
return (...args: any[]) => {
if (timer) {
clearTimeout(timer)
timer = null
}
timer = setTimeout(() => {
fn.apply(null, args)
}, wait)
}
}
validate.ts
export function validateNameInput(input: string) {
if (!input || !/^[a-zA-Z_0-9]+$/gm.test(input)) {
return new Error('只支持大小写字母、数字、下划线!')
}
return true
}
export const validate = [
{ required: true, message: '订阅配置名称为必填项', trigger: 'blur' },
{ pattern: /^[a-zA-Z_0-9-]+$/, message: '只能输入英文、数字、下划线、中划线' },
]
history.pushState(null, null, document.URL);
window.addEventListener('popstate', function () {
history.pushState(null, null, document.URL);
});
localstorage.ts
import { err } from './logger'
export const setLocal = (key: string, value: string) => {
localStorage.setItem(key, value)
}
export const getLocal = (key: string, isParse?: boolean) => {
const value = localStorage.getItem(key)
if (isParse) {
try {
return JSON.parse(value)
} catch (error) {
err(error)
return value
}
}
return value
}
logger.ts
const log = (value: any) => {
console.log(value)
}
const info = (value: any) => {
console.info(value)
}
const warn = (value: any) => {
console.warn(value)
}
const err = (value: any) => {
console.error(value)
}
export default {
log,
info,
warn,
err
}
export {
log,
info,
warn,
err
}
mapParse.ts
export const decodeLine = (encoded: any) => {
const len = encoded.length
let index = 0
const array = []
let lat = 0
let lng = 0
try {
while (index < len) {
let b
let shift = 0
let result = 0
do {
b = encoded.charCodeAt(index++) - 63
result |= (b & 0x1f) << shift
shift += 5
}
while (b >= 0x20)
const dlat = ((result & 1) ? ~(result >> 1) : (result >> 1))
lat += dlat
shift = 0
result = 0
do {
b = encoded.charCodeAt(index++) - 63
result |= (b & 0x1f) << shift
shift += 5
}
while (b >= 0x20)
const dlng = ((result & 1) ? ~(result >> 1) : (result >> 1))
lng += dlng
array.push(lng / 100000 + ','+ lat / 100000)
}
} catch(ex) {
return []
}
return array
}
export const lngLatStringToLatLngLiteral = (str: any) => {
const arr = str.split(',')
arr[0] = Number(arr[0])
arr[1] = Number(arr[1])
return {
lat: Number(arr[1]),
lng: Number(arr[0])
}
}
request.ts
import axios from 'axios'
import config from '../config/path'
const { baseUrl } = config
interface ResI {
status: number
data: any
}
export default async (
url: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
body = {},
config = {}
): Promise<ResI | any> => {
const isGet = method === 'GET'
try {
const res: ResI = await axios(`${baseUrl}${url}`, {
method,
...(isGet ? {
params: body
} : {
data: body
}),
...config,
...{timeout: 60000}
})
const { status, data } = res
return status === 200 ? data : null
} catch (error) {
console.error(error)
return null
}
}
tsconfig.json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"types": [
"webpack-env"
],
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
tslint.json
{
"defaultSeverity": "warning",
"extend": [
"tslint: recommended"
],
"linterOptions": {
"exclude": [
"node_modules/**"
]
},
"rules": {
"indent": [true, "spaces", 2],
"interface-name": false,
"no-consecutive-blank-lines": false,
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [true, "single"],
"semicolon": false,
"trailing-comma": false,
"variable-name": [
false
],
"no-empty": false,
"member-access": [true, "no-public"],
"no-console": false,
"triple-equals": false,
"max-line-length": [ false ],
"object-literal-key-quotes": [true, "consistent"],
"no-string-literal": false,
"prefer-const": false,
"strict-boolean-expressions": false,
"no-unused-expression": false,
"no-shadowed-variable": false
}
}
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
const NODE_ENV = process.env.NODE_ENV
const outDir = NODE_ENV === 'test' ? 'test' : 'dist'
export default defineConfig({
plugins: [vue()],
base: './',
build: {
outDir,
assetsDir: 'assets',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@views': path.resolve(__dirname, 'src/views'),
}
},
server: {
https: false,
open: true,
host: '0.0.0.0',
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8038',
changeOrigin: true,
secure: false,
ws: true,
}
}
},
define: {
'process.env': {
NODE_ENV
}
},
optimizeDeps: {
include: []
}
})
pm2.json
{
"apps": [{
"name" : "dinet-mysql-curd",
"script" : "./server/production/app.js",
"instances" : "4",
"log_date_format" : "YYYY-MM-DD HH-mm-ss",
"max_memory_restart": "300M",
"error_file": "./server/logs/node_std/stderr.log",
"out_file": "./server/logs/node_std/stdout.log",
"pid_file": "./server/logs/pid/tt.pid",
"merge_logs" : false,
"exec_mode" : "cluster_mode",
"env": {
"NODE_ENV": "production"
},
"env_production": {
"NODE_ENV": "production"
}
}]
}
package.json
{
"name": "dinet-mysql-curd",
"version": "0.0.0",
"scripts": {
"dev": "NODE_ENV=dev vite",
"build": "NODE_ENV=production vite build",
"serve": "vite preview",
"server": "ts-node-dev --project server/tsconfig.json ./server/development/app.ts",
"start": "tsc --project server/tsconfig.json && pm2 start pm2.json --env production",
"stop": "pm2 stop pm2.json"
},
"dependencies": {
"ant-design-vue": "^3.0.0-alpha.11",
"axios": "^0.24.0",
"dayjs": "^1.10.7",
"koa": "^2.13.4",
"koa-bodyparser": "^4.3.0",
"koa-mount": "^4.0.0",
"koa-router": "^10.1.1",
"koa-session": "^6.2.0",
"koa-static": "^5.0.0",
"lodash-es": "^4.17.21",
"mysql2": "^2.3.3",
"node-fetch": "^3.1.0",
"pm2": "^5.1.2",
"sequelize": "^6.9.0",
"sequelize-cli": "^6.3.0",
"ts-node-dev": "^1.1.8",
"vue": "^3.0.5",
"vue-router": "^4.0.12"
},
"devDependencies": {
"@types/koa": "^2.13.4",
"@types/koa-bodyparser": "^4.3.4",
"@types/koa-mount": "^4.0.1",
"@types/koa-router": "^7.4.4",
"@types/koa-session": "^5.10.4",
"@types/koa-static": "^4.0.2",
"@types/lodash-es": "^4.17.5",
"@types/node-fetch": "^3.0.3",
"@types/sequelize": "^4.28.10",
"@vitejs/plugin-vue": "^1.2.5",
"@vue/compiler-sfc": "^3.0.5",
"less": "^4.1.2",
"less-loader": "^10.2.0",
"typescript": "^4.3.2",
"vite": "^2.4.3",
"vue-tsc": "^0.0.24"
}
}
useElMenu.ts
import { Menu } from 'ant-design-vue'
import { Ref, ref, onMounted, watch, computed } from 'vue'
import { useRoute } from 'vue-router'
export const useMenuWithRoutr = () => {
const route = useRoute()
const isCollapse = ref(false)
const menuRef = ref(null) as Ref<typeof Menu | null>
const openKeys = ref([''])
const selectedKeys = ref([''])
const sRouter = computed(() => {
const { path, meta } = route
return { path, meta }
})
const onCollapse = () => {
isCollapse.value = !isCollapse.value
}
const onActiveMenu = () => {
const { path, meta } = sRouter.value
const { index } = meta
if (!index) return
openKeys.value = [(index as string)]
selectedKeys.value = [path]
}
watch(sRouter, () => {
onActiveMenu()
})
onMounted(onActiveMenu)
return {
isCollapse,
onCollapse,
menuRef,
onActiveMenu,
openKeys,
selectedKeys
}
}