Merge branch 'master' of https://github.com/taoran1401/tools-web into seo
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'dev'
|
||||
NODE_ENV = 'development'
|
||||
# 网站名称
|
||||
VITE_APP_TITLE = '工具坊'
|
||||
# 网站域名
|
||||
|
||||
@@ -81,6 +81,24 @@ function getTools() {
|
||||
url: '/signimage',
|
||||
cateId: 5,
|
||||
cate: '设计工具',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '随机密码生成',
|
||||
logo: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
|
||||
desc: '密码生成器、随机字符串生成,批量生成',
|
||||
url: '/random_password',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: 'URL编码/解码',
|
||||
logo: 'https://fuss10.elemecdn.com/a/3f/3302e58f9a181d2509f3dc0fa68b0jpeg.jpeg',
|
||||
desc: 'URL在线编码解码工具(UrlEncode编码 和 UrlDecode解码)',
|
||||
url: '/urlencode',
|
||||
cateId: 2,
|
||||
cate: '开发运维',
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -107,6 +125,10 @@ function getToolsCate() {
|
||||
{
|
||||
id: 6,
|
||||
title: '查询相关'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '其他'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+6
-6
@@ -8,23 +8,23 @@ import Right from '@/components/Layout/Right/Right.vue'
|
||||
<Header></Header>
|
||||
<div class="w-full flex flex-col justify-between mb-20">
|
||||
<el-row>
|
||||
<el-col :xl="4" :md="1" class=""></el-col>
|
||||
<el-col :xl="16" :md="22" class="mt-4">
|
||||
<el-col :lg="4" :sm="1" class=""></el-col>
|
||||
<el-col :lg="16" :sm="22" class="mt-4">
|
||||
<el-row class="flex-1">
|
||||
<el-col :xl="19" :md="19" class="">
|
||||
<el-col :lg="19" :sm="19" class="">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="animation" mode="out-in">
|
||||
<component :is="Component" :key="route.path"></component>
|
||||
</transition>
|
||||
</router-view>
|
||||
</el-col>
|
||||
<el-col :xl="5" :md="5" class="">
|
||||
<el-col :lg="5" :sm="5" class="">
|
||||
<Right/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<!-- <el-col :xl="4" :md="1" class="flex bg-gray-200"></el-col> -->
|
||||
<el-col :xl="4" :md="1"></el-col>
|
||||
<!-- <el-col :lg="4" :sm="1" class="flex bg-gray-200"></el-col> -->
|
||||
<el-col :lg="4" :sm="1"></el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<Floor></Floor>
|
||||
|
||||
@@ -27,13 +27,13 @@ onMounted(() => {
|
||||
<template>
|
||||
<div>
|
||||
<!-- card -->
|
||||
<div class="mr-3 flex justify-between flex-wrap self-card-div" :gutter="10">
|
||||
<router-link v-for="(item, index) in toolsStore.list" :key="index" :to="item.url" class="flex flex-col mt-3 border-solid rounded border-gray w-[32.5%] p-2 bg-gray-100 hover:shadow-md">
|
||||
<div class="mr-3 flex justify-between flex-wrap self-card-div c-xs:ml-3" :gutter="10">
|
||||
<router-link v-for="(item, index) in toolsStore.list" :key="index" :to="item.url" class="flex flex-col mt-3 border-solid rounded border-gray w-[32.5%] p-2 bg-gray-100 hover:shadow-md c-xs:w-[99.5%]">
|
||||
<div class="flex items-center border-b pb-2">
|
||||
<el-image :src="item.logo" class="w-10 h-10 min-h-[2.5rem] min-w-[2.5rem] rounded-full"></el-image>
|
||||
<div class="flex flex-col ml-2 w-full">
|
||||
<div class="flex">
|
||||
<div class="font-semibold text-lg ">{{ item.title }}</div>
|
||||
<div class="font-semibold text-lg line-clamp-1">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<el-text size="small">{{ item.cate }}</el-text>
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { Search } from '@element-plus/icons-vue';
|
||||
import { Search,MoreFilled } from '@element-plus/icons-vue';
|
||||
import { useToolsStore } from '@/store/modules/tools'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import 'element-plus/theme-chalk/display.css'
|
||||
|
||||
const appName = ref(import.meta.env.VITE_APP_TITLE || '工具坊')
|
||||
const appNet = ref('tools.ranblogs.com')
|
||||
const isNavDrawer = ref(false)
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
//查询参数
|
||||
@@ -52,10 +54,10 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<header class="">
|
||||
<nav class="h-28 w-full leading-10 flex flex-col justify-between pt-2 pb-2">
|
||||
<nav class="h-28 w-full leading-10 flex flex-col justify-between pt-2 pb-2 c-xs:h-16 c-xs:border-b-[1px]">
|
||||
<el-row>
|
||||
<el-col :xl="4" :md="1"></el-col>
|
||||
<el-col :xl="16" :md="22">
|
||||
<el-col :lg="4" :sm="1"></el-col>
|
||||
<el-col :lg="16" :sm="22">
|
||||
<div class="flex items-center justify-between">
|
||||
<!-- left -->
|
||||
<router-link class="ml-4 flex flex-row" to="/">
|
||||
@@ -67,7 +69,7 @@ onMounted(() => {
|
||||
</router-link>
|
||||
|
||||
<!-- right -->
|
||||
<div class=" w-full md:w-auto flex hidden md:block">
|
||||
<div class=" w-full md:w-auto flex c-xs:hidden md:block">
|
||||
<ul class="flex mt-4 flex-col md:flex-row md:mt-0 justify-end items-center">
|
||||
<li class="hover:text-blue-500">
|
||||
<el-button link size="large" @click="collect">收藏本站</el-button>
|
||||
@@ -77,10 +79,54 @@ onMounted(() => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- h5: 搜索、导航 -->
|
||||
<div class="c-lg:hidden c-sm:hidden">
|
||||
<div class="mr-3 leading-3">
|
||||
<!-- <el-icon size="25"><Search /></el-icon> -->
|
||||
<el-icon size="25" class="ml-2" style="transform:rotate(90deg)" @click="isNavDrawer == false ? isNavDrawer = true : isNavDrawer = false"><MoreFilled /></el-icon>
|
||||
</div>
|
||||
<!-- 抽屉 -->
|
||||
<el-drawer v-model="isNavDrawer" direction="rtl" class="" size="70%">
|
||||
<div>
|
||||
<!-- search -->
|
||||
<div>
|
||||
<el-input
|
||||
v-model="searchParam.title"
|
||||
placeholder="搜索工具"
|
||||
class="w-full max-w-xl h-10"
|
||||
@keyup.enter.native="search">
|
||||
<template #append>
|
||||
<el-button :icon="Search" @click="search"/>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<!-- cates -->
|
||||
<div class="mt-3 text-center">
|
||||
<ul class="flex flex-col rounded items-center">
|
||||
<li class="flex items-center w-full text-center mb-2">
|
||||
<div
|
||||
class="hover:text-blue-400 text-xl w-full hover:bg-gray-200 hover:rounded-md h-10 leading-10" :class="searchParam.cateId == 0 ? 'text-blue-400 bg-gray-200 rounded-md' : ''"
|
||||
@click="chooseCate(0)"
|
||||
>全部工具</div>
|
||||
</li>
|
||||
<li v-for="(item, index) in toolsStore.cates" :key="index" class="flex items-center mb-2 w-full">
|
||||
<!-- <el-text>{{ index != 0 ? ' ' : '' }} </el-text> -->
|
||||
<div
|
||||
class="hover:text-blue-400 text-xl w-full hover:bg-gray-200 hover:rounded-md h-10 leading-10" :class="searchParam.cateId == item.id ? 'text-blue-400 bg-gray-200 rounded-md' : ''"
|
||||
@click="chooseCate(item.id)"
|
||||
>{{ item.title }}</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-row class="mt-3">
|
||||
<el-col :xl="19" :md="19">
|
||||
<!-- pc/pad:搜索、导航 -->
|
||||
<el-row class="mt-3 hidden-xs-only">
|
||||
<el-col :lg="19" :sm="19">
|
||||
<!-- cates -->
|
||||
<div class="mr-3">
|
||||
<ul class="flex bg-gray-100 h-10 rounded leading-10 pl-3">
|
||||
@@ -100,7 +146,7 @@ onMounted(() => {
|
||||
</ul>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xl="5" :md="5">
|
||||
<el-col :lg="5" :sm="5">
|
||||
<!-- search -->
|
||||
<div>
|
||||
<el-input
|
||||
@@ -116,7 +162,7 @@ onMounted(() => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :xl="4" :md="1" class="flex"></el-col>
|
||||
<el-col :lg="4" :sm="1" class="flex"></el-col>
|
||||
</el-row>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
@@ -32,7 +32,7 @@ const info = reactive({
|
||||
|
||||
|
||||
<!-- hot tools -->
|
||||
<div class="mt-3 border-solid rounded border-gray border p-3">
|
||||
<div class="mt-3 border-solid rounded border-gray border p-3 c-xs:mr-3 c-xs:ml-3">
|
||||
<div class="text-xl text-gray-400 font-bold">热门工具</div>
|
||||
<ul class="mt-3">
|
||||
<li class="flex items-center hover:bg-gray-200 p-1 rounded" v-for="(item, index) in info.hotTools" :key="index">
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy, genRandomStrByChars } from '@/utils/string'
|
||||
import { ElMessage } from 'element-plus'
|
||||
const info = reactive({
|
||||
title: "随机密码生成",
|
||||
char: '',
|
||||
pwLen: 10,
|
||||
pwNum: 5,
|
||||
resStr: '',
|
||||
autosize: {
|
||||
minRows: 5
|
||||
},
|
||||
checkedUpper: true,
|
||||
checkedLower: true,
|
||||
checkedNum: true,
|
||||
checkedSign: false,
|
||||
charNum: '0123456789',
|
||||
charUpper: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
charLower: 'abcdefghijklmnopqrstuvwxyz',
|
||||
cahrSign: '~!@#$%^&*()_+',
|
||||
})
|
||||
|
||||
//生成密码
|
||||
const gen = () => {
|
||||
//包含字符验证
|
||||
if (info.char == '') {
|
||||
ElMessage({
|
||||
message: "包含字符不能为空",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
|
||||
//生成长度验证
|
||||
if (info.pwLen > 100) {
|
||||
ElMessage({
|
||||
message: "生成长度范围在1~100",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
|
||||
//生成数量验证
|
||||
if (info.pwNum > 100) {
|
||||
ElMessage({
|
||||
message: "生成数量范围在1~100",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
|
||||
//清空旧数据
|
||||
info.resStr = ''
|
||||
|
||||
//按数量生成密码
|
||||
for (let i = 1; i <= info.pwNum; i++) {
|
||||
let ext = '\n'
|
||||
if (i == info.pwNum) {
|
||||
ext = ''
|
||||
}
|
||||
info.resStr += genRandomStrByChars(info.char, info.pwLen) + ext
|
||||
}
|
||||
}
|
||||
|
||||
const changeCheckBox = (val: any, type: number) => {
|
||||
switch(type) {
|
||||
case 0:
|
||||
//设置数字字符
|
||||
setChar(val, /\d+/g, info.charNum)
|
||||
break;
|
||||
case 1:
|
||||
//设置小写字母字符
|
||||
setChar(val, /[a-z]/g, info.charLower)
|
||||
break;
|
||||
case 2:
|
||||
//设置大写字母字符
|
||||
setChar(val, /[A-Z]/g, info.charUpper)
|
||||
break;
|
||||
case 3:
|
||||
//设置特殊符号字符
|
||||
setChar(val, /[~!@#$%^&*()_+]/g, info.cahrSign)
|
||||
break;
|
||||
default:
|
||||
info.char = ''
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//设置字符
|
||||
const setChar = (val: boolean, reg: RegExp, charType: string) => {
|
||||
if (val == true) {
|
||||
//添加
|
||||
info.char += charType
|
||||
} else {
|
||||
//清除
|
||||
info.char = info.char.replace(reg, "")
|
||||
}
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async (resStr: string) => {
|
||||
copy(resStr)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
//设置初始字符
|
||||
changeCheckBox(info.checkedNum, 0)
|
||||
changeCheckBox(info.checkedLower, 1)
|
||||
changeCheckBox(info.checkedUpper, 2)
|
||||
changeCheckBox(info.checkedSign, 3)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div>
|
||||
<el-text>密码组合</el-text>
|
||||
<div>
|
||||
<el-checkbox v-model="info.checkedNum" label="数字(0-9)" @change="(val: any) => (changeCheckBox(val, 0))"/>
|
||||
<el-checkbox v-model="info.checkedLower" label="小写字母(a-z)" @change="(val: any) => (changeCheckBox(val, 1))"/>
|
||||
<el-checkbox v-model="info.checkedUpper" label="大写字母(A-Z)" @change="(val: any) => (changeCheckBox(val, 2))"/>
|
||||
<el-checkbox v-model="info.checkedSign" label="其他符号(~!@#$%^&*()-+_=,.)" @change="(val: any) => (changeCheckBox(val, 3))"/>
|
||||
</div>
|
||||
<div>
|
||||
<el-input class="" v-model="info.char">
|
||||
<template #prepend>包含字符:</template>
|
||||
</el-input>
|
||||
<el-input v-model="info.pwLen" placeholder="长度范围1~100" class="mt-3" max="100" type="number">
|
||||
<template #prepend>生成长度:</template>
|
||||
</el-input>
|
||||
<el-input v-model="info.pwNum" placeholder="长度范围1~100" class="mt-3" max="100" type="number">
|
||||
<template #prepend>生成数量:</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="mt-3 mb-3">
|
||||
<el-button type="primary" @click="gen">生成密码</el-button>
|
||||
<el-button type="primary" @click="copyRes(info.resStr)">复制全部</el-button>
|
||||
</div>
|
||||
<!-- res -->
|
||||
<div>
|
||||
<el-input type="textarea" :autosize="info.autosize" v-model="info.resStr"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy } from '@/utils/string'
|
||||
|
||||
const info = reactive({
|
||||
title: "URL编码/解码",
|
||||
content: '',
|
||||
tranRes: '',
|
||||
})
|
||||
|
||||
|
||||
//编码
|
||||
const toEncode = () => {
|
||||
info.tranRes = ''
|
||||
info.tranRes = encodeURIComponent(info.content)
|
||||
}
|
||||
|
||||
//解码
|
||||
const toDecode = () => {
|
||||
info.tranRes = ''
|
||||
info.tranRes = decodeURIComponent(info.content)
|
||||
}
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
info.content = ''
|
||||
info.tranRes = ''
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async () => {
|
||||
copy(info.tranRes)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 ml-4 flex-1 mr-10">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div>
|
||||
<div>
|
||||
<el-input type="textarea" :rows="8" v-model="info.content"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-button type="primary" @click="toEncode">UrlEncode编码</el-button>
|
||||
<el-button type="primary" @click="toDecode">UrlDecode解码</el-button>
|
||||
<el-button type="primary" @click="copyRes">复制结果</el-button>
|
||||
<el-button type="danger" @click="clear">清空内容</el-button>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3">
|
||||
<el-input type="textarea" :rows="8" v-model="info.tranRes"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -80,6 +80,22 @@ export const constantRoute = [
|
||||
title: "在线编辑图片"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/random_password',
|
||||
component: () => import('@/components/RandomPassword/RandomPassword.vue'),
|
||||
name: 'random_password',
|
||||
meta: {
|
||||
title: "随机密码生成"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/urlencode',
|
||||
component: () => import('@/components/UrlEncode/UrlEncode.vue'),
|
||||
name: 'urlencode',
|
||||
meta: {
|
||||
title: "URL编码/解码"
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: () => import('@/components/404/404.vue'),
|
||||
|
||||
+18
-1
@@ -50,9 +50,26 @@ export function copy(resStr: string) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按指定字符生成随机字符串(场景:生成随机密码)
|
||||
*
|
||||
* @param char
|
||||
* @param length
|
||||
* @returns
|
||||
*/
|
||||
export function genRandomStrByChars(chars: string, length: number): string {
|
||||
let password = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
const randomIndex = Math.floor(Math.random() * chars.length);
|
||||
password += chars[randomIndex];
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
const StringUtils = {
|
||||
transferred,
|
||||
copy
|
||||
copy,
|
||||
genRandomStrByChars
|
||||
}
|
||||
|
||||
export default StringUtils
|
||||
@@ -1,4 +1,5 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
@@ -6,6 +7,13 @@ export default {
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
screens: {
|
||||
// 自定义响应式尺寸
|
||||
'c-xs': {'max': '768px'},
|
||||
'c-sm': {'min': '768px'},
|
||||
'c-lg': {'min': '1200px'},
|
||||
...defaultTheme.screens,
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user