fix: 添加隐私权政策,工具组件目录调整
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
<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: "ASCII码表",
|
||||
})
|
||||
// ascii: https://ascii.co.uk/table
|
||||
const tableDataControlCodes = reactive([
|
||||
{dec:"0",oct:"000",hex:"0",code:"NUL",desc:"空字符"},
|
||||
{dec:"1",oct:"001",hex:"1",code:"SOH",desc:"标题开始"},
|
||||
{dec:"2",oct:"002",hex:"2",code:"STX",desc:"正文开始"},
|
||||
{dec:"3",oct:"003",hex:"3",code:"ETX",desc:"正文结束"},
|
||||
{dec:"4",oct:"004",hex:"4",code:"EOT",desc:"传输结束"},
|
||||
{dec:"5",oct:"005",hex:"5",code:"ENQ",desc:"询问"},
|
||||
{dec:"6",oct:"006",hex:"6",code:"ACK",desc:"收到通知"},
|
||||
{dec:"7",oct:"007",hex:"7",code:"BEL",desc:"铃"},
|
||||
{dec:"8",oct:"010",hex:"8",code:"BS",desc:"退格"},
|
||||
{dec:"9",oct:"011",hex:"9",code:"HT",desc:"水平制表符"},
|
||||
{dec:"10",oct:"012",hex:"A",code:"LF",desc:"换行键"},
|
||||
{dec:"11",oct:"013",hex:"B",code:"VT",desc:"垂直制表符"},
|
||||
{dec:"12",oct:"014",hex:"C",code:"FF",desc:"换页键"},
|
||||
{dec:"13",oct:"015",hex:"D",code:"CR",desc:"回车键"},
|
||||
{dec:"14",oct:"016",hex:"E",code:"SO",desc:"移出"},
|
||||
{dec:"15",oct:"017",hex:"F",code:"SI",desc:"移入"},
|
||||
{dec:"16",oct:"020",hex:"10",code:"DLE",desc:"数据链路转义"},
|
||||
{dec:"17",oct:"021",hex:"11",code:"DC1",desc:"设备控制 1"},
|
||||
{dec:"18",oct:"022",hex:"12",code:"DC2",desc:"设备控制 2"},
|
||||
{dec:"19",oct:"023",hex:"13",code:"DC3",desc:"设备控制 3"},
|
||||
{dec:"20",oct:"024",hex:"14",code:"DC4",desc:"设备控制 4"},
|
||||
{dec:"21",oct:"025",hex:"15",code:"NAK",desc:"拒绝接收"},
|
||||
{dec:"22",oct:"026",hex:"16",code:"SYN",desc:"同步空闲"},
|
||||
{dec:"23",oct:"027",hex:"17",code:"ETB",desc:"传输块结束"},
|
||||
{dec:"24",oct:"030",hex:"18",code:"CAN",desc:"取消"},
|
||||
{dec:"25",oct:"031",hex:"19",code:"EM",desc:"介质中断"},
|
||||
{dec:"26",oct:"032",hex:"1A",code:"SUB",desc:"替换"},
|
||||
{dec:"27",oct:"033",hex:"1B",code:"ESC",desc:"换码符"},
|
||||
{dec:"28",oct:"034",hex:"1C",code:"FS",desc:"文件分隔符"},
|
||||
{dec:"29",oct:"035",hex:"1D",code:"GS",desc:"组分隔符"},
|
||||
{dec:"30",oct:"036",hex:"1E",code:"RS",desc:"记录分离符"},
|
||||
{dec:"31",oct:"037",hex:"1F",code:"US",desc:"单元分隔符"}
|
||||
])
|
||||
|
||||
const tableDataStandard = reactive([
|
||||
{dec:"32",oct:"040",hex:"20",code:"Space",desc:"空格"},
|
||||
{dec:"33",oct:"041",hex:"21",code:"!",desc:""},
|
||||
{dec:"34",oct:"042",hex:"22",code:"\"",desc:""},
|
||||
{dec:"35",oct:"043",hex:"23",code:"#",desc:""},
|
||||
{dec:"36",oct:"044",hex:"24",code:"$",desc:""},
|
||||
{dec:"37",oct:"045",hex:"25",code:"%",desc:""},
|
||||
{dec:"38",oct:"046",hex:"26",code:"&",desc:""},
|
||||
{dec:"40",oct:"050",hex:"28",code:"(",desc:""},
|
||||
{dec:"41",oct:"051",hex:"29",code:")",desc:""},
|
||||
{dec:"42",oct:"052",hex:"2A",code:"*",desc:""},
|
||||
{dec:"43",oct:"053",hex:"2B",code:"+",desc:""},
|
||||
{dec:"44",oct:"054",hex:"2C",code:",",desc:""},
|
||||
{dec:"45",oct:"055",hex:"2D",code:"-",desc:""},
|
||||
{dec:"46",oct:"056",hex:"2E",code:".",desc:""},
|
||||
{dec:"47",oct:"057",hex:"2F",code:"\/",desc:""},
|
||||
{dec:"48",oct:"060",hex:"30",code:"0",desc:""},
|
||||
{dec:"49",oct:"061",hex:"31",code:"1",desc:""},
|
||||
{dec:"50",oct:"062",hex:"32",code:"2",desc:""},
|
||||
{dec:"51",oct:"063",hex:"33",code:"3",desc:""},
|
||||
{dec:"52",oct:"064",hex:"34",code:"4",desc:""},
|
||||
{dec:"53",oct:"065",hex:"35",code:"5",desc:""},
|
||||
{dec:"54",oct:"066",hex:"36",code:"6",desc:""},
|
||||
{dec:"55",oct:"067",hex:"37",code:"7",desc:""},
|
||||
{dec:"56",oct:"070",hex:"38",code:"8",desc:""},
|
||||
{dec:"57",oct:"071",hex:"39",code:"9",desc:""},
|
||||
{dec:"58",oct:"072",hex:"3A",code:":",desc:""},
|
||||
{dec:"59",oct:"073",hex:"3B",code:";",desc:""},
|
||||
{dec:"60",oct:"074",hex:"3C",code:"<",desc:""},
|
||||
{dec:"61",oct:"075",hex:"3D",code:"=",desc:""},
|
||||
{dec:"62",oct:"076",hex:"3E",code:">",desc:""},
|
||||
{dec:"63",oct:"077",hex:"3F",code:"?",desc:""},
|
||||
{dec:"64",oct:"100",hex:"40",code:"@",desc:""},
|
||||
{dec:"65",oct:"101",hex:"41",code:"A",desc:""},
|
||||
{dec:"66",oct:"102",hex:"42",code:"B",desc:""},
|
||||
{dec:"67",oct:"103",hex:"43",code:"C",desc:""},
|
||||
{dec:"68",oct:"104",hex:"44",code:"D",desc:""},
|
||||
{dec:"69",oct:"105",hex:"45",code:"E",desc:""},
|
||||
{dec:"70",oct:"106",hex:"46",code:"F",desc:""},
|
||||
{dec:"71",oct:"107",hex:"47",code:"G",desc:""},
|
||||
{dec:"72",oct:"110",hex:"48",code:"H",desc:""},
|
||||
{dec:"73",oct:"111",hex:"49",code:"I",desc:""},
|
||||
{dec:"74",oct:"112",hex:"4A",code:"J",desc:""},
|
||||
{dec:"75",oct:"113",hex:"4B",code:"K",desc:""},
|
||||
{dec:"76",oct:"114",hex:"4C",code:"L",desc:""},
|
||||
{dec:"77",oct:"115",hex:"4D",code:"M",desc:""},
|
||||
{dec:"78",oct:"116",hex:"4E",code:"N",desc:""},
|
||||
{dec:"79",oct:"117",hex:"4F",code:"O",desc:""},
|
||||
{dec:"80",oct:"120",hex:"50",code:"P",desc:""},
|
||||
{dec:"81",oct:"121",hex:"51",code:"Q",desc:""},
|
||||
{dec:"82",oct:"122",hex:"52",code:"R",desc:""},
|
||||
{dec:"83",oct:"123",hex:"53",code:"S",desc:""},
|
||||
{dec:"84",oct:"124",hex:"54",code:"T",desc:""},
|
||||
{dec:"85",oct:"125",hex:"55",code:"U",desc:""},
|
||||
{dec:"86",oct:"126",hex:"56",code:"V",desc:""},
|
||||
{dec:"87",oct:"127",hex:"57",code:"W",desc:""},
|
||||
{dec:"88",oct:"130",hex:"58",code:"X",desc:""},
|
||||
{dec:"89",oct:"131",hex:"59",code:"Y",desc:""},
|
||||
{dec:"90",oct:"132",hex:"5A",code:"Z",desc:""},
|
||||
{dec:"91",oct:"133",hex:"5B",code:"[",desc:""},
|
||||
{dec:"92",oct:"134",hex:"5C",code:"\\",desc:""},
|
||||
{dec:"93",oct:"135",hex:"5D",code:"]",desc:""},
|
||||
{dec:"94",oct:"136",hex:"5E",code:"^",desc:""},
|
||||
{dec:"95",oct:"137",hex:"5F",code:"_",desc:""},
|
||||
{dec:"96",oct:"140",hex:"60",code:"`",desc:""},
|
||||
{dec:"97",oct:"141",hex:"61",code:"a",desc:""},
|
||||
{dec:"98",oct:"142",hex:"62",code:"b",desc:""},
|
||||
{dec:"99",oct:"143",hex:"63",code:"c",desc:""},
|
||||
{dec:"100",oct:"144",hex:"64",code:"d",desc:""},
|
||||
{dec:"101",oct:"145",hex:"65",code:"e",desc:""},
|
||||
{dec:"102",oct:"146",hex:"66",code:"f",desc:""},
|
||||
{dec:"103",oct:"147",hex:"67",code:"g",desc:""},
|
||||
{dec:"104",oct:"150",hex:"68",code:"h",desc:""},
|
||||
{dec:"105",oct:"151",hex:"69",code:"i",desc:""},
|
||||
{dec:"106",oct:"152",hex:"6A",code:"j",desc:""},
|
||||
{dec:"107",oct:"153",hex:"6B",code:"k",desc:""},
|
||||
{dec:"108",oct:"154",hex:"6C",code:"l",desc:""},
|
||||
{dec:"109",oct:"155",hex:"6D",code:"m",desc:""},
|
||||
{dec:"110",oct:"156",hex:"6E",code:"n",desc:""},
|
||||
{dec:"111",oct:"157",hex:"6F",code:"o",desc:""},
|
||||
{dec:"112",oct:"160",hex:"70",code:"p",desc:""},
|
||||
{dec:"113",oct:"161",hex:"71",code:"q",desc:""},
|
||||
{dec:"114",oct:"162",hex:"72",code:"r",desc:""},
|
||||
{dec:"115",oct:"163",hex:"73",code:"s",desc:""},
|
||||
{dec:"116",oct:"164",hex:"74",code:"t",desc:""},
|
||||
{dec:"117",oct:"165",hex:"75",code:"u",desc:""},
|
||||
{dec:"118",oct:"166",hex:"76",code:"v",desc:""},
|
||||
{dec:"119",oct:"167",hex:"77",code:"w",desc:""},
|
||||
{dec:"120",oct:"170",hex:"78",code:"x",desc:""},
|
||||
{dec:"121",oct:"171",hex:"79",code:"y",desc:""},
|
||||
{dec:"122",oct:"172",hex:"7A",code:"z",desc:""},
|
||||
{dec:"123",oct:"173",hex:"7B",code:"{",desc:""},
|
||||
{dec:"124",oct:"174",hex:"7C",code:"|",desc:""},
|
||||
{dec:"125",oct:"175",hex:"7D",code:"},",desc:""},
|
||||
{dec:"126",oct:"176",hex:"7E",code:"~",desc:""},
|
||||
{dec:"127",oct:"177",hex:"7F",code:"DEL",desc:"删除"},
|
||||
])
|
||||
|
||||
const tableDataNonStandard = reactive([
|
||||
{dec:"128",oct:"200",hex:"80",code:"\u20ac",desc:""},
|
||||
{dec:"129",oct:"201",hex:"81",code:"\u0081",desc:""},
|
||||
{dec:"130",oct:"202",hex:"82",code:"\u201a",desc:""},
|
||||
{dec:"131",oct:"203",hex:"83",code:"\u0192",desc:""},
|
||||
{dec:"132",oct:"204",hex:"84",code:"\u201e",desc:""},
|
||||
{dec:"133",oct:"205",hex:"85",code:"\u2026",desc:""},
|
||||
{dec:"134",oct:"206",hex:"86",code:"\u2020",desc:""},
|
||||
{dec:"135",oct:"207",hex:"87",code:"\u2021",desc:""},
|
||||
{dec:"136",oct:"210",hex:"88",code:"\u02c6",desc:""},
|
||||
{dec:"137",oct:"211",hex:"89",code:"\u2030",desc:""},
|
||||
{dec:"138",oct:"212",hex:"8A",code:"\u0160",desc:""},
|
||||
{dec:"139",oct:"213",hex:"8B",code:"\u2039",desc:""},
|
||||
{dec:"140",oct:"214",hex:"8C",code:"\u0152",desc:""},
|
||||
{dec:"141",oct:"215",hex:"8D",code:"\u008d",desc:""},
|
||||
{dec:"142",oct:"216",hex:"8E",code:"\u017d",desc:""},
|
||||
{dec:"143",oct:"217",hex:"8F",code:"\u008f",desc:""},
|
||||
{dec:"144",oct:"220",hex:"90",code:"\u0090",desc:""},
|
||||
{dec:"145",oct:"221",hex:"91",code:"\u2018",desc:""},
|
||||
{dec:"146",oct:"222",hex:"92",code:"\u2019",desc:""},
|
||||
{dec:"147",oct:"223",hex:"93",code:"\u201c",desc:""},
|
||||
{dec:"148",oct:"224",hex:"94",code:"\u201d",desc:""},
|
||||
{dec:"149",oct:"225",hex:"95",code:"\u2022",desc:""},
|
||||
{dec:"150",oct:"226",hex:"96",code:"\u2013",desc:""},
|
||||
{dec:"151",oct:"227",hex:"97",code:"\u2014",desc:""},
|
||||
{dec:"152",oct:"230",hex:"98",code:"\u02dc",desc:""},
|
||||
{dec:"153",oct:"231",hex:"99",code:"\u2122",desc:""},
|
||||
{dec:"154",oct:"232",hex:"9A",code:"\u0161",desc:""},
|
||||
{dec:"155",oct:"233",hex:"9B",code:"\u203a",desc:""},
|
||||
{dec:"156",oct:"234",hex:"9C",code:"\u0153",desc:""},
|
||||
{dec:"157",oct:"235",hex:"9D",code:"\u009d",desc:""},
|
||||
{dec:"158",oct:"236",hex:"9E",code:"\u017e",desc:""},
|
||||
{dec:"159",oct:"237",hex:"9F",code:"\u0178",desc:""},
|
||||
{dec:"160",oct:"240",hex:"A0",code:" ",desc:"\u63d2\u5165\u7a7a\u683c"},
|
||||
{dec:"161",oct:"241",hex:"A1",code:"\u00a1",desc:""},
|
||||
{dec:"162",oct:"242",hex:"A2",code:"\u00a2",desc:""},
|
||||
{dec:"163",oct:"243",hex:"A3",code:"\u00a3",desc:""},
|
||||
{dec:"164",oct:"244",hex:"A4",code:"\u00a4",desc:""},
|
||||
{dec:"165",oct:"245",hex:"A5",code:"\u00a5",desc:""},
|
||||
{dec:"166",oct:"246",hex:"A6",code:"\u00a6",desc:""},
|
||||
{dec:"167",oct:"247",hex:"A7",code:"\u00a7",desc:""},
|
||||
{dec:"168",oct:"250",hex:"A8",code:"\u00a8",desc:""},
|
||||
{dec:"169",oct:"251",hex:"A9",code:"\u00a9",desc:""},
|
||||
{dec:"170",oct:"252",hex:"AA",code:"\u00aa",desc:""},
|
||||
{dec:"171",oct:"253",hex:"AB",code:"\u00ab",desc:""},
|
||||
{dec:"172",oct:"254",hex:"AC",code:"\u00ac",desc:""},
|
||||
{dec:"173",oct:"255",hex:"AD",code:"\u00ad",desc:""},
|
||||
{dec:"174",oct:"256",hex:"AE",code:"\u00ae",desc:""},
|
||||
{dec:"175",oct:"257",hex:"AF",code:"\u00af",desc:""},
|
||||
{dec:"176",oct:"260",hex:"B0",code:"\u00b0",desc:""},
|
||||
{dec:"177",oct:"261",hex:"B1",code:"\u00b1",desc:""},
|
||||
{dec:"178",oct:"262",hex:"B2",code:"\u00b2",desc:""},
|
||||
{dec:"179",oct:"263",hex:"B3",code:"\u00b3",desc:""},
|
||||
{dec:"180",oct:"264",hex:"B4",code:"\u00b4",desc:""},
|
||||
{dec:"181",oct:"265",hex:"B5",code:"\u00b5",desc:""},
|
||||
{dec:"182",oct:"266",hex:"B6",code:"\u00b6",desc:""},
|
||||
{dec:"183",oct:"267",hex:"B7",code:"\u00b7",desc:""},
|
||||
{dec:"184",oct:"270",hex:"B8",code:"\u00b8",desc:""},
|
||||
{dec:"185",oct:"271",hex:"B9",code:"\u00b9",desc:""},
|
||||
{dec:"186",oct:"272",hex:"BA",code:"\u00ba",desc:""},
|
||||
{dec:"187",oct:"273",hex:"BB",code:"\u00bb",desc:""},
|
||||
{dec:"188",oct:"274",hex:"BC",code:"\u00bc",desc:""},
|
||||
{dec:"189",oct:"275",hex:"BD",code:"\u00bd",desc:""},
|
||||
{dec:"190",oct:"276",hex:"BE",code:"\u00be",desc:""},
|
||||
{dec:"191",oct:"277",hex:"BF",code:"\u00bf",desc:""},
|
||||
{dec:"192",oct:"300",hex:"C0",code:"\u00c0",desc:""},
|
||||
{dec:"193",oct:"301",hex:"C1",code:"\u00c1",desc:""},
|
||||
{dec:"194",oct:"302",hex:"C2",code:"\u00c2",desc:""},
|
||||
{dec:"195",oct:"303",hex:"C3",code:"\u00c3",desc:""},
|
||||
{dec:"196",oct:"304",hex:"C4",code:"\u00c4",desc:""},
|
||||
{dec:"197",oct:"305",hex:"C5",code:"\u00c5",desc:""},
|
||||
{dec:"198",oct:"306",hex:"C6",code:"\u00c6",desc:""},
|
||||
{dec:"199",oct:"307",hex:"C7",code:"\u00c7",desc:""},
|
||||
{dec:"200",oct:"310",hex:"C8",code:"\u00c8",desc:""},
|
||||
{dec:"201",oct:"311",hex:"C9",code:"\u00c9",desc:""},
|
||||
{dec:"202",oct:"312",hex:"CA",code:"\u00ca",desc:""},
|
||||
{dec:"203",oct:"313",hex:"CB",code:"\u00cb",desc:""},
|
||||
{dec:"204",oct:"314",hex:"CC",code:"\u00cc",desc:""},
|
||||
{dec:"205",oct:"315",hex:"CD",code:"\u00cd",desc:""},
|
||||
{dec:"206",oct:"316",hex:"CE",code:"\u00ce",desc:""},
|
||||
{dec:"207",oct:"317",hex:"CF",code:"\u00cf",desc:""},
|
||||
{dec:"208",oct:"320",hex:"D0",code:"\u00d0",desc:""},
|
||||
{dec:"209",oct:"321",hex:"D1",code:"\u00d1",desc:""},
|
||||
{dec:"210",oct:"322",hex:"D2",code:"\u00d2",desc:""},
|
||||
{dec:"211",oct:"323",hex:"D3",code:"\u00d3",desc:""},
|
||||
{dec:"212",oct:"324",hex:"D4",code:"\u00d4",desc:""},
|
||||
{dec:"213",oct:"325",hex:"D5",code:"\u00d5",desc:""},
|
||||
{dec:"214",oct:"326",hex:"D6",code:"\u00d6",desc:""},
|
||||
{dec:"215",oct:"327",hex:"D7",code:"\u00d7",desc:""},
|
||||
{dec:"216",oct:"330",hex:"D8",code:"\u00d8",desc:""},
|
||||
{dec:"217",oct:"331",hex:"D9",code:"\u00d9",desc:""},
|
||||
{dec:"218",oct:"332",hex:"DA",code:"\u00da",desc:""},
|
||||
{dec:"219",oct:"333",hex:"DB",code:"\u00db",desc:""},
|
||||
{dec:"220",oct:"334",hex:"DC",code:"\u00dc",desc:""},
|
||||
{dec:"221",oct:"335",hex:"DD",code:"\u00dd",desc:""},
|
||||
{dec:"222",oct:"336",hex:"DE",code:"\u00de",desc:""},
|
||||
{dec:"223",oct:"337",hex:"DF",code:"\u00df",desc:""},
|
||||
{dec:"224",oct:"340",hex:"E0",code:"\u00e0",desc:""},
|
||||
{dec:"225",oct:"341",hex:"E1",code:"\u00e1",desc:""},
|
||||
{dec:"226",oct:"342",hex:"E2",code:"\u00e2",desc:""},
|
||||
{dec:"227",oct:"343",hex:"E3",code:"\u00e3",desc:""},
|
||||
{dec:"228",oct:"344",hex:"E4",code:"\u00e4",desc:""},
|
||||
{dec:"229",oct:"345",hex:"E5",code:"\u00e5",desc:""},
|
||||
{dec:"230",oct:"346",hex:"E6",code:"\u00e6",desc:""},
|
||||
{dec:"231",oct:"347",hex:"E7",code:"\u00e7",desc:""},
|
||||
{dec:"232",oct:"350",hex:"E8",code:"\u00e8",desc:""},
|
||||
{dec:"233",oct:"351",hex:"E9",code:"\u00e9",desc:""},
|
||||
{dec:"234",oct:"352",hex:"EA",code:"\u00ea",desc:""},
|
||||
{dec:"235",oct:"353",hex:"EB",code:"\u00eb",desc:""},
|
||||
{dec:"236",oct:"354",hex:"EC",code:"\u00ec",desc:""},
|
||||
{dec:"237",oct:"355",hex:"ED",code:"\u00ed",desc:""},
|
||||
{dec:"238",oct:"356",hex:"EE",code:"\u00ee",desc:""},
|
||||
{dec:"239",oct:"357",hex:"EF",code:"\u00ef",desc:""},
|
||||
{dec:"240",oct:"360",hex:"F0",code:"\u00f0",desc:""},
|
||||
{dec:"241",oct:"361",hex:"F1",code:"\u00f1",desc:""},
|
||||
{dec:"242",oct:"362",hex:"F2",code:"\u00f2",desc:""},
|
||||
{dec:"243",oct:"363",hex:"F3",code:"\u00f3",desc:""},
|
||||
{dec:"244",oct:"364",hex:"F4",code:"\u00f4",desc:""},
|
||||
{dec:"245",oct:"365",hex:"F5",code:"\u00f5",desc:""},
|
||||
{dec:"246",oct:"366",hex:"F6",code:"\u00f6",desc:""},
|
||||
{dec:"247",oct:"367",hex:"F7",code:"\u00f7",desc:""},
|
||||
{dec:"248",oct:"370",hex:"F8",code:"\u00f8",desc:""},
|
||||
{dec:"249",oct:"371",hex:"F9",code:"\u00f9",desc:""},
|
||||
{dec:"250",oct:"372",hex:"FA",code:"\u00fa",desc:""},
|
||||
{dec:"251",oct:"373",hex:"FB",code:"\u00fb",desc:""},
|
||||
{dec:"252",oct:"374",hex:"FC",code:"\u00fc",desc:""},
|
||||
{dec:"253",oct:"375",hex:"FD",code:"\u00fd",desc:""},
|
||||
{dec:"254",oct:"376",hex:"FE",code:"\u00fe",desc:""},
|
||||
{dec:"255",oct:"377",hex:"FF",code:"\u00ff",desc:""},
|
||||
])
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<el-table :data="tableDataControlCodes" class="w-full">
|
||||
<el-table-column label="ASCII 控制字符 (字符编码: 0-31)" align="center">
|
||||
<el-table-column prop="dec" label="十进制DEC" width="120" />
|
||||
<el-table-column prop="oct" label="八进制OCT" width="120" />
|
||||
<el-table-column prop="hex" label="十六进制HEX" width="120" />
|
||||
<el-table-column prop="code" label="符号" width="120" />
|
||||
<el-table-column prop="desc" label="描述" />
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-table :data="tableDataStandard" class="w-full mt-3">
|
||||
<el-table-column label="ASCII 打印字符 (字符编码: 32-127)" align="center">
|
||||
<el-table-column prop="dec" label="十进制DEC" width="120" />
|
||||
<el-table-column prop="oct" label="八进制OCT" width="120" />
|
||||
<el-table-column prop="hex" label="十六进制HEX" width="120" />
|
||||
<el-table-column prop="code" label="符号" width="120" />
|
||||
<el-table-column prop="desc" label="描述" />
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-table :data="tableDataNonStandard" class="w-full mt-3">
|
||||
<el-table-column label="ASCII扩展码 (字符编码: 128-255)" align="center">
|
||||
<el-table-column prop="dec" label="十进制DEC" width="120" />
|
||||
<el-table-column prop="oct" label="八进制OCT" width="120" />
|
||||
<el-table-column prop="hex" label="十六进制HEX" width="120" />
|
||||
<el-table-column prop="code" label="符号" width="120" />
|
||||
<el-table-column prop="desc" label="描述" />
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,124 @@
|
||||
<script setup lang="ts">
|
||||
import { onBeforeMount, reactive,ref } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { ElMessage } from 'element-plus'
|
||||
import vueDanmaku from 'vue3-danmaku' //https://www.npmjs.com/package/vue3-danmaku
|
||||
const info = reactive({
|
||||
title: "手持弹幕",
|
||||
content: '工具坊',
|
||||
barrage: [] as string[],
|
||||
speed: 200,
|
||||
textSize: 500,
|
||||
textColor: '#FFFFFF',
|
||||
bgColor: '#000000',
|
||||
channels: 1,//轨道数量
|
||||
extraStyle: '',
|
||||
danmakuFullStyle: "",
|
||||
isPlay: false,
|
||||
danmakuFullHeight: '100%',
|
||||
danmakuFullWidth: '100%',
|
||||
})
|
||||
|
||||
const danmakuFullRef = ref(null) as any
|
||||
|
||||
//全屏播放/双击退出全屏并暂停
|
||||
const fullScreenPlay = () => {
|
||||
if (info.isPlay) {
|
||||
setExtraStyle('visibility: hidden;')
|
||||
//暂停
|
||||
danmakuFullRef.value.resize()
|
||||
danmakuFullRef.value.stop()
|
||||
// danmakuFullRef.value.pause()
|
||||
info.isPlay = false
|
||||
} else {
|
||||
setExtraStyle()
|
||||
//播放
|
||||
formatBarrage()
|
||||
danmakuFullRef.value.resize()
|
||||
danmakuFullRef.value.play()
|
||||
info.isPlay = true
|
||||
}
|
||||
}
|
||||
|
||||
//设置额外样式
|
||||
const setExtraStyle = (danmakuStyleExt: string = '') => {
|
||||
info.extraStyle = "color: " + info.textColor + ";font-size: " + info.textSize + "px"
|
||||
info.danmakuFullStyle = "z-index: 99; position: fixed; top: 0px; left: 0px; height:" + info.danmakuFullHeight + "; width:" + info.danmakuFullWidth + "; background-color:" + info.bgColor + ";" + danmakuStyleExt
|
||||
}
|
||||
|
||||
//格式化弹幕
|
||||
const formatBarrage = () => {
|
||||
info.barrage[0] = info.content
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
setExtraStyle('visibility: hidden;')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<!-- 全屏 -->
|
||||
<vue-danmaku
|
||||
ref="danmakuFullRef"
|
||||
v-model:danmus="info.barrage"
|
||||
loop
|
||||
:autoplay="false"
|
||||
:speeds="info.speed"
|
||||
:channels="info.channels"
|
||||
:extraStyle='info.extraStyle'
|
||||
:style="info.danmakuFullStyle"
|
||||
@dblclick="fullScreenPlay"
|
||||
:useSlot="true"
|
||||
>
|
||||
<!-- 弹幕slot -->
|
||||
<template v-slot:dm="{ danmu }">
|
||||
<div class="" :style="info.extraStyle">
|
||||
<!-- <img class="img" :src="danmu.avatar" /> -->
|
||||
<!-- <span>{{ index }}{{ danmu.name }}:</span> -->
|
||||
<span>{{ danmu }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</vue-danmaku>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="flex mb-2">
|
||||
<el-text class="w-20">弹幕内容:</el-text>
|
||||
<div class="w-72"><el-input v-model="info.content" type="textarea" rows="3"></el-input></div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-2">
|
||||
<el-text class="w-20">播放速度:</el-text>
|
||||
<div class="w-72 ml-2"><el-slider v-model="info.speed" :min="1" :max="500"/></div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-2">
|
||||
<el-text class="w-20">文字大小:</el-text>
|
||||
<div class="w-72 ml-2"><el-slider v-model="info.textSize" :min="12" :max="1000"/></div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-2">
|
||||
<el-text class="w-20">文字颜色:</el-text>
|
||||
<div><el-color-picker v-model="info.textColor" size="large" @change="setExtraStyle()"/></div>
|
||||
</div>
|
||||
|
||||
<div class="flex mb-2">
|
||||
<el-text class="w-20">背景颜色:</el-text>
|
||||
<div><el-color-picker v-model="info.bgColor" size="large" @change="setExtraStyle()"/></div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-button @click="fullScreenPlay" type="primary" class="mr-3">{{ info.isPlay == false ? '播放' : '暂停'}}</el-button>
|
||||
<el-text>双击可退出弹幕</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.vue-danmaku .danmus .dm{
|
||||
top: 50% !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,479 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, onMounted } from 'vue'
|
||||
import Spreadsheet from 'x-data-spreadsheet'
|
||||
import 'x-data-spreadsheet/dist/locale/zh-cn';
|
||||
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
import { toEchartsData, toSpreadsheetData } from '@/utils/echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import * as XLSX from 'xlsx'
|
||||
const info = reactive({
|
||||
title: "柱状图",
|
||||
})
|
||||
|
||||
const chartDom = ref<HTMLElement|null>()
|
||||
const myChart = ref<echarts.ECharts>()
|
||||
const dataFileRef = ref()
|
||||
|
||||
const setOptionName = ref(1)
|
||||
//缩放比例
|
||||
const sacleSize = ref(100)
|
||||
//画布宽高
|
||||
const widthCanvas = ref(720)
|
||||
const heightCanvas = ref(400)
|
||||
//下载文件类型
|
||||
const downType = ref('1')
|
||||
//图形属性颜色
|
||||
const attrColor = ref('#5470c6')
|
||||
//标题位置
|
||||
const titlePos = ref('center')
|
||||
//标题
|
||||
const title = ref('然部落在线工具')
|
||||
//副标题
|
||||
const subTitle = ref('在线图表制作工具')
|
||||
//显示标题 - 开关
|
||||
const titleSwitch = ref(true)
|
||||
//显示副标题 - 开关
|
||||
const subTitleSwitch = ref(true)
|
||||
|
||||
/** 水印 */
|
||||
const watermarkSwitch = ref(false) //开关
|
||||
const waterMarkText = ref('然部落工具');
|
||||
|
||||
//创建水印
|
||||
const createWatermark = () => {
|
||||
let canvas = document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
canvas.width = canvas.height = 100;
|
||||
ctx!.textAlign = 'center';
|
||||
ctx!.textBaseline = 'middle';
|
||||
ctx!.globalAlpha = 0.08;
|
||||
ctx!.font = '20px Microsoft Yahei';
|
||||
ctx!.translate(50, 50);
|
||||
ctx!.rotate(-Math.PI / 4);
|
||||
ctx!.fillText(waterMarkText.value, 0, 0);
|
||||
return canvas
|
||||
}
|
||||
|
||||
//操作图表
|
||||
const canvasHandle = (type) => {
|
||||
// let element = document.getElementById('main')
|
||||
let element = chartDom.value
|
||||
switch(type) {
|
||||
case "scale":
|
||||
//缩放画布
|
||||
let scale = sacleSize.value / 100
|
||||
element!.style.transform = `scale(${scale})`
|
||||
break
|
||||
case "size":
|
||||
//图表尺寸
|
||||
element!.style.width = widthCanvas.value + 'px';
|
||||
element!.style.height = heightCanvas.value + 'px';
|
||||
reloadCanvas()
|
||||
break
|
||||
case "title":
|
||||
let fakerTitle = ''
|
||||
let fakerSubTitle = ''
|
||||
//修改标题相关配置
|
||||
if (titleSwitch.value === true) {
|
||||
fakerTitle = title.value
|
||||
}
|
||||
if (subTitleSwitch.value === true) {
|
||||
fakerSubTitle = subTitle.value
|
||||
}
|
||||
myChart.value?.setOption({
|
||||
title: {
|
||||
text: fakerTitle,
|
||||
subtext: fakerSubTitle,
|
||||
left: titlePos.value
|
||||
}
|
||||
})
|
||||
break
|
||||
case "color":
|
||||
//图表属性颜色
|
||||
myChart.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
itemStyle: {
|
||||
color: attrColor.value
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
case "watermark":
|
||||
//水印
|
||||
if (watermarkSwitch.value === true) {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: {
|
||||
image: createWatermark(),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: '#fff'
|
||||
})
|
||||
}
|
||||
break;
|
||||
case "data":
|
||||
//更新数据
|
||||
myChart.value?.setOption({
|
||||
xAxis: {
|
||||
data: colunmData.value,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: valueData.value,
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
const handleChange = () => {
|
||||
|
||||
}
|
||||
|
||||
//数据
|
||||
const colunmData = ref(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']);
|
||||
const valueData = ref(['23', '24', '18', '25', '27', '28', '25']);
|
||||
//选项
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: {
|
||||
// // type: 'pattern',
|
||||
// // image: canvas,
|
||||
// // repeat: 'repeat'
|
||||
// },
|
||||
title: {
|
||||
text: title.value,
|
||||
subtext: subTitle.value,
|
||||
left: titlePos.value
|
||||
},
|
||||
xAxis: {
|
||||
data: colunmData.value,
|
||||
},
|
||||
yAxis: {},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: valueData.value,
|
||||
itemStyle: {
|
||||
color: attrColor.value
|
||||
}
|
||||
}
|
||||
],
|
||||
tooltip: {}
|
||||
};
|
||||
|
||||
//重新加载画布
|
||||
const reloadCanvas = () => {
|
||||
myChart.value = echarts.init(chartDom.value)
|
||||
myChart.value.clear();
|
||||
myChart.value.resize({
|
||||
width: widthCanvas.value,
|
||||
height: heightCanvas.value
|
||||
})
|
||||
myChart.value.setOption(option)
|
||||
}
|
||||
|
||||
//下载echarts图表图片
|
||||
const downEchartsImg = () => {
|
||||
let ext = downType.value == '1' ? 'png' : 'jpeg'
|
||||
let imgUrl = myChart.value?.getDataURL({
|
||||
type: ext as "png" | 'jpeg',
|
||||
pixelRatio: 2,
|
||||
})
|
||||
|
||||
if (imgUrl != undefined) {
|
||||
var downloadLink = document.createElement('a');
|
||||
downloadLink.href = imgUrl;
|
||||
downloadLink.download = 'echart.' + ext;
|
||||
downloadLink.click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 上传、编辑数据 */
|
||||
//抽屉开关
|
||||
const drawer = ref(false)
|
||||
const rowsData = ref({})
|
||||
|
||||
const editData = () => {
|
||||
if (drawer.value == true) {
|
||||
drawer.value = false
|
||||
} else {
|
||||
drawer.value = true
|
||||
/** 在线excel */
|
||||
Spreadsheet.locale('zh-cn', (window.x_spreadsheet as any).$messages['zh-cn']);
|
||||
//const sheet = new Spreadsheet("#x-spreadsheet", {
|
||||
new Spreadsheet("#x-spreadsheet", {
|
||||
showToolbar: false, //隐藏顶部工具栏
|
||||
showBottomBar: false,//隐藏底部工具栏,
|
||||
view: {
|
||||
height: () => document.documentElement.clientHeight / 2,
|
||||
width: () => document.documentElement.clientWidth,
|
||||
}
|
||||
})
|
||||
.loadData({
|
||||
//样式
|
||||
styles: [
|
||||
{
|
||||
bgcolor: '#f4f5f8',
|
||||
textwrap: true,
|
||||
color: '#900b09',
|
||||
border: {
|
||||
top: ['thin', '#0366d6'],
|
||||
bottom: ['thin', '#0366d6'],
|
||||
right: ['thin', '#0366d6'],
|
||||
left: ['thin', '#0366d6'],
|
||||
},
|
||||
},
|
||||
],
|
||||
//数据
|
||||
rows: rowsData.value
|
||||
}) // load data
|
||||
.change(data => {
|
||||
//表格数据改变后触发
|
||||
//规则: 获取第一列和第二列的数据
|
||||
let tmp = toEchartsData(data)
|
||||
colunmData.value = tmp[0]
|
||||
valueData.value = tmp[1]
|
||||
canvasHandle('data')
|
||||
});
|
||||
// sheet.validate()
|
||||
}
|
||||
}
|
||||
|
||||
//上传数据文件
|
||||
const fileList = ref()
|
||||
const updateDataFile = async (params) => {
|
||||
const _file = params.file;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = (ev) => {
|
||||
try {
|
||||
if (!ev.target) {
|
||||
return
|
||||
}
|
||||
const data = ev.target.result;
|
||||
const workbook = XLSX.read(data, {
|
||||
type: 'binary'
|
||||
});
|
||||
let useCount = 0;
|
||||
let tmpColumnData:any[] = [];
|
||||
let tmpValueData:any[] = [];
|
||||
for (let sheet in workbook.Sheets) {
|
||||
//这里只需要第一个sheet
|
||||
if (useCount > 0) {
|
||||
continue;
|
||||
}
|
||||
//循环读取每个文件
|
||||
const sheetArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {header: ['0', '1']});
|
||||
//若当前sheet没有数据,则continue
|
||||
if(sheetArray.length == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
for(let item in sheetArray){
|
||||
if (sheetArray[item] != undefined) {
|
||||
let tmp1 = sheetArray as []
|
||||
tmpColumnData.push(tmp1[item][0])
|
||||
tmpValueData.push(tmp1[item][1])
|
||||
}
|
||||
}
|
||||
useCount++
|
||||
}
|
||||
//更新数据
|
||||
colunmData.value = tmpColumnData
|
||||
valueData.value = tmpValueData
|
||||
//更新图表
|
||||
canvasHandle('data')
|
||||
//更新表格
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
} catch (e) {
|
||||
console.log('error')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
fileReader.readAsArrayBuffer(_file)
|
||||
}
|
||||
|
||||
//当超出限制时,执行的钩子函数
|
||||
//这里覆盖前一个文件
|
||||
const handleExceed: UploadProps['onExceed'] = (files) => {
|
||||
dataFileRef.value!.clearFiles()
|
||||
const file = files[0] as UploadRawFile
|
||||
file.uid = genFileId()
|
||||
dataFileRef.value!.handleStart(file)
|
||||
dataFileRef.value!.submit()
|
||||
}
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
//init echart dom
|
||||
chartDom.value = document.getElementById('main')
|
||||
//设置画布宽高
|
||||
canvasHandle('size')
|
||||
//reload canvas
|
||||
reloadCanvas()
|
||||
//数据格式转换成
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
// console.log(rowsData.value)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl flex">
|
||||
<div class="w-4/6">
|
||||
<div class="flex flex-row-reverse mb-4">
|
||||
<el-input-number v-model="sacleSize" :min="1" :max="100" :step="5" @change="canvasHandle('scale')" step-strictly/>
|
||||
<el-text>缩放:</el-text>
|
||||
</div>
|
||||
<!-- <div class="flex justify-center items-center"> -->
|
||||
<div class="flex justify-center items-center max-h-[500px] max-w-[1000px] overflow-auto">
|
||||
<div id="main" class="bg-white"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-2/6 bg-white ml-3 rounded-lg p-3">
|
||||
<div class="mb-3">
|
||||
<el-button class="w-full mb-3" type="primary" size="large" @click="downEchartsImg">下载图表</el-button>
|
||||
<div class="flex items-center ml-1">
|
||||
<el-text>文件类型</el-text>
|
||||
<el-radio-group v-model="downType" class="ml-4">
|
||||
<el-radio size="large" label="1">PNG</el-radio>
|
||||
<el-radio size="large" label="2">JPEG</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="支持上传:xls, xlsx, csv文件"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button class="w-1/2">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
class="dataFileRef flex"
|
||||
ref="dataFileRef"
|
||||
accept=".xls,.xlsx,.csv"
|
||||
:http-request="updateDataFile"
|
||||
:on-exceed="handleExceed"
|
||||
:limit="1"
|
||||
>
|
||||
上传数据
|
||||
</el-upload>
|
||||
</el-button>
|
||||
|
||||
</el-tooltip>
|
||||
<el-button class="w-1/2" @click="editData">编辑数据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse v-model="setOptionName" @change="handleChange" accordion>
|
||||
<el-collapse-item title="画布设置" name="1">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">宽度</el-text>
|
||||
<el-input-number
|
||||
v-model="widthCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex mt-3">
|
||||
<el-text class="w-16 text-right">高度</el-text>
|
||||
<el-input-number
|
||||
v-model="heightCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="标题设置" name="2">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">位置</el-text>
|
||||
<el-radio-group v-model="titlePos" class="ml-3" @change="canvasHandle('title')">
|
||||
<el-radio-button label="left" value="left">左</el-radio-button>
|
||||
<el-radio-button label="center" value="center">中</el-radio-button>
|
||||
<el-radio-button label="right" value="right">右</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-divider content-position="left" class="pl-10">标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="titleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="title" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
<el-divider content-position="left">副标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="subTitleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">副标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="subTitle" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="图形属性" name="3">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">颜色</el-text>
|
||||
<el-color-picker v-model="attrColor" class="ml-3" @change="canvasHandle('color')"/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="水印设置" name="4">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="watermarkSwitch" class="ml-3" @change="canvasHandle('watermark')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">水印内容</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="waterMarkText" @change="canvasHandle('watermark')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" custom-class="sheet" style="">
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-drawer__body){
|
||||
display: none !important;
|
||||
}
|
||||
:deep(.el-drawer__header){
|
||||
display: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
:deep(.el-drawer){
|
||||
height: 50% !important;
|
||||
}
|
||||
:deep(.el-upload-list){
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,479 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, onMounted } from 'vue'
|
||||
import Spreadsheet from 'x-data-spreadsheet'
|
||||
import 'x-data-spreadsheet/dist/locale/zh-cn';
|
||||
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
import { toEchartsData, toSpreadsheetData } from '@/utils/echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import * as XLSX from 'xlsx'
|
||||
const info = reactive({
|
||||
title: "折线图",
|
||||
})
|
||||
|
||||
const chartDom = ref<HTMLElement|null>()
|
||||
const myChart = ref<echarts.ECharts>()
|
||||
const dataFileRef = ref()
|
||||
|
||||
const setOptionName = ref(1)
|
||||
//缩放比例
|
||||
const sacleSize = ref(100)
|
||||
//画布宽高
|
||||
const widthCanvas = ref(720)
|
||||
const heightCanvas = ref(400)
|
||||
//下载文件类型
|
||||
const downType = ref('1')
|
||||
//图形属性颜色
|
||||
const attrColor = ref('#5470c6')
|
||||
//标题位置
|
||||
const titlePos = ref('center')
|
||||
//标题
|
||||
const title = ref('然部落在线工具')
|
||||
//副标题
|
||||
const subTitle = ref('在线图表制作工具')
|
||||
//显示标题 - 开关
|
||||
const titleSwitch = ref(true)
|
||||
//显示副标题 - 开关
|
||||
const subTitleSwitch = ref(true)
|
||||
|
||||
/** 水印 */
|
||||
const watermarkSwitch = ref(false) //开关
|
||||
const waterMarkText = ref('然部落工具');
|
||||
|
||||
//创建水印
|
||||
const createWatermark = () => {
|
||||
let canvas = document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
canvas.width = canvas.height = 100;
|
||||
ctx!.textAlign = 'center';
|
||||
ctx!.textBaseline = 'middle';
|
||||
ctx!.globalAlpha = 0.08;
|
||||
ctx!.font = '20px Microsoft Yahei';
|
||||
ctx!.translate(50, 50);
|
||||
ctx!.rotate(-Math.PI / 4);
|
||||
ctx!.fillText(waterMarkText.value, 0, 0);
|
||||
return canvas
|
||||
}
|
||||
|
||||
//操作图表
|
||||
const canvasHandle = (type) => {
|
||||
// let element = document.getElementById('main')
|
||||
let element = chartDom.value
|
||||
switch(type) {
|
||||
case "scale":
|
||||
//缩放画布
|
||||
let scale = sacleSize.value / 100
|
||||
element!.style.transform = `scale(${scale})`
|
||||
break
|
||||
case "size":
|
||||
//图表尺寸
|
||||
element!.style.width = widthCanvas.value + 'px';
|
||||
element!.style.height = heightCanvas.value + 'px';
|
||||
reloadCanvas()
|
||||
break
|
||||
case "title":
|
||||
let fakerTitle = ''
|
||||
let fakerSubTitle = ''
|
||||
//修改标题相关配置
|
||||
if (titleSwitch.value === true) {
|
||||
fakerTitle = title.value
|
||||
}
|
||||
if (subTitleSwitch.value === true) {
|
||||
fakerSubTitle = subTitle.value
|
||||
}
|
||||
myChart.value?.setOption({
|
||||
title: {
|
||||
text: fakerTitle,
|
||||
subtext: fakerSubTitle,
|
||||
left: titlePos.value
|
||||
}
|
||||
})
|
||||
break
|
||||
case "color":
|
||||
//图表属性颜色
|
||||
myChart.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
itemStyle: {
|
||||
color: attrColor.value
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
case "watermark":
|
||||
//水印
|
||||
if (watermarkSwitch.value === true) {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: {
|
||||
image: createWatermark(),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: '#fff'
|
||||
})
|
||||
}
|
||||
break;
|
||||
case "data":
|
||||
//更新数据
|
||||
myChart.value?.setOption({
|
||||
xAxis: {
|
||||
data: colunmData.value,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: valueData.value,
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
const handleChange = () => {
|
||||
|
||||
}
|
||||
|
||||
//数据
|
||||
const colunmData = ref(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']);
|
||||
const valueData = ref(['23', '24', '18', '25', '27', '28', '25']);
|
||||
//选项
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: {
|
||||
// // type: 'pattern',
|
||||
// // image: canvas,
|
||||
// // repeat: 'repeat'
|
||||
// },
|
||||
title: {
|
||||
text: title.value,
|
||||
subtext: subTitle.value,
|
||||
left: titlePos.value
|
||||
},
|
||||
xAxis: {
|
||||
data: colunmData.value,
|
||||
},
|
||||
yAxis: {},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
data: valueData.value,
|
||||
itemStyle: {
|
||||
color: attrColor.value
|
||||
}
|
||||
}
|
||||
],
|
||||
tooltip: {}
|
||||
};
|
||||
|
||||
//重新加载画布
|
||||
const reloadCanvas = () => {
|
||||
myChart.value = echarts.init(chartDom.value)
|
||||
myChart.value.clear();
|
||||
myChart.value.resize({
|
||||
width: widthCanvas.value,
|
||||
height: heightCanvas.value
|
||||
})
|
||||
myChart.value.setOption(option)
|
||||
}
|
||||
|
||||
//下载echarts图表图片
|
||||
const downEchartsImg = () => {
|
||||
let ext = downType.value == '1' ? 'png' : 'jpeg'
|
||||
let imgUrl = myChart.value?.getDataURL({
|
||||
type: ext as "png" | 'jpeg',
|
||||
pixelRatio: 2,
|
||||
})
|
||||
|
||||
if (imgUrl != undefined) {
|
||||
var downloadLink = document.createElement('a');
|
||||
downloadLink.href = imgUrl;
|
||||
downloadLink.download = 'echart.' + ext;
|
||||
downloadLink.click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 上传、编辑数据 */
|
||||
//抽屉开关
|
||||
const drawer = ref(false)
|
||||
const rowsData = ref({})
|
||||
|
||||
const editData = () => {
|
||||
if (drawer.value == true) {
|
||||
drawer.value = false
|
||||
} else {
|
||||
drawer.value = true
|
||||
/** 在线excel */
|
||||
Spreadsheet.locale('zh-cn', (window.x_spreadsheet as any).$messages['zh-cn']);
|
||||
//const sheet = new Spreadsheet("#x-spreadsheet", {
|
||||
new Spreadsheet("#x-spreadsheet", {
|
||||
showToolbar: false, //隐藏顶部工具栏
|
||||
showBottomBar: false,//隐藏底部工具栏,
|
||||
view: {
|
||||
height: () => document.documentElement.clientHeight / 2,
|
||||
width: () => document.documentElement.clientWidth,
|
||||
}
|
||||
})
|
||||
.loadData({
|
||||
//样式
|
||||
styles: [
|
||||
{
|
||||
bgcolor: '#f4f5f8',
|
||||
textwrap: true,
|
||||
color: '#900b09',
|
||||
border: {
|
||||
top: ['thin', '#0366d6'],
|
||||
bottom: ['thin', '#0366d6'],
|
||||
right: ['thin', '#0366d6'],
|
||||
left: ['thin', '#0366d6'],
|
||||
},
|
||||
},
|
||||
],
|
||||
//数据
|
||||
rows: rowsData.value
|
||||
}) // load data
|
||||
.change(data => {
|
||||
//表格数据改变后触发
|
||||
//规则: 获取第一列和第二列的数据
|
||||
let tmp = toEchartsData(data)
|
||||
colunmData.value = tmp[0]
|
||||
valueData.value = tmp[1]
|
||||
canvasHandle('data')
|
||||
});
|
||||
// sheet.validate()
|
||||
}
|
||||
}
|
||||
|
||||
//上传数据文件
|
||||
const fileList = ref()
|
||||
const updateDataFile = async (params) => {
|
||||
const _file = params.file;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = (ev) => {
|
||||
try {
|
||||
if (!ev.target) {
|
||||
return
|
||||
}
|
||||
const data = ev.target.result;
|
||||
const workbook = XLSX.read(data, {
|
||||
type: 'binary'
|
||||
});
|
||||
let useCount = 0;
|
||||
let tmpColumnData:any[] = [];
|
||||
let tmpValueData:any[] = [];
|
||||
for (let sheet in workbook.Sheets) {
|
||||
//这里只需要第一个sheet
|
||||
if (useCount > 0) {
|
||||
continue;
|
||||
}
|
||||
//循环读取每个文件
|
||||
const sheetArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {header: ['0', '1']});
|
||||
//若当前sheet没有数据,则continue
|
||||
if(sheetArray.length == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
for(let item in sheetArray){
|
||||
if (sheetArray[item] != undefined) {
|
||||
let tmp1 = sheetArray as []
|
||||
tmpColumnData.push(tmp1[item][0])
|
||||
tmpValueData.push(tmp1[item][1])
|
||||
}
|
||||
}
|
||||
useCount++
|
||||
}
|
||||
//更新数据
|
||||
colunmData.value = tmpColumnData
|
||||
valueData.value = tmpValueData
|
||||
//更新图表
|
||||
canvasHandle('data')
|
||||
//更新表格
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
} catch (e) {
|
||||
console.log('error')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
fileReader.readAsArrayBuffer(_file)
|
||||
}
|
||||
|
||||
//当超出限制时,执行的钩子函数
|
||||
//这里覆盖前一个文件
|
||||
const handleExceed: UploadProps['onExceed'] = (files) => {
|
||||
dataFileRef.value!.clearFiles()
|
||||
const file = files[0] as UploadRawFile
|
||||
file.uid = genFileId()
|
||||
dataFileRef.value!.handleStart(file)
|
||||
dataFileRef.value!.submit()
|
||||
}
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
//init echart dom
|
||||
chartDom.value = document.getElementById('main')
|
||||
//设置画布宽高
|
||||
canvasHandle('size')
|
||||
//reload canvas
|
||||
reloadCanvas()
|
||||
//数据格式转换成
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
// console.log(rowsData.value)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl flex">
|
||||
<div class="w-4/6">
|
||||
<div class="flex flex-row-reverse mb-4">
|
||||
<el-input-number v-model="sacleSize" :min="1" :max="100" :step="5" @change="canvasHandle('scale')" step-strictly/>
|
||||
<el-text>缩放:</el-text>
|
||||
</div>
|
||||
<!-- <div class="flex justify-center items-center"> -->
|
||||
<div class="flex justify-center items-center max-h-[500px] max-w-[1000px] overflow-auto">
|
||||
<div id="main" class="bg-white"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-2/6 bg-white ml-3 rounded-lg p-3">
|
||||
<div class="mb-3">
|
||||
<el-button class="w-full mb-3" type="primary" size="large" @click="downEchartsImg">下载图表</el-button>
|
||||
<div class="flex items-center ml-1">
|
||||
<el-text>文件类型</el-text>
|
||||
<el-radio-group v-model="downType" class="ml-4">
|
||||
<el-radio size="large" label="1">PNG</el-radio>
|
||||
<el-radio size="large" label="2">JPEG</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="支持上传:xls, xlsx, csv文件"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button class="w-1/2">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
class="dataFileRef flex"
|
||||
ref="dataFileRef"
|
||||
accept=".xls,.xlsx,.csv"
|
||||
:http-request="updateDataFile"
|
||||
:on-exceed="handleExceed"
|
||||
:limit="1"
|
||||
>
|
||||
上传数据
|
||||
</el-upload>
|
||||
</el-button>
|
||||
|
||||
</el-tooltip>
|
||||
<el-button class="w-1/2" @click="editData">编辑数据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse v-model="setOptionName" @change="handleChange" accordion>
|
||||
<el-collapse-item title="画布设置" name="1">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">宽度</el-text>
|
||||
<el-input-number
|
||||
v-model="widthCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex mt-3">
|
||||
<el-text class="w-16 text-right">高度</el-text>
|
||||
<el-input-number
|
||||
v-model="heightCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="标题设置" name="2">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">位置</el-text>
|
||||
<el-radio-group v-model="titlePos" class="ml-3" @change="canvasHandle('title')">
|
||||
<el-radio-button label="left" value="left">左</el-radio-button>
|
||||
<el-radio-button label="center" value="center">中</el-radio-button>
|
||||
<el-radio-button label="right" value="right">右</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-divider content-position="left" class="pl-10">标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="titleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="title" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
<el-divider content-position="left">副标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="subTitleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">副标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="subTitle" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="图形属性" name="3">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">颜色</el-text>
|
||||
<el-color-picker v-model="attrColor" class="ml-3" @change="canvasHandle('color')"/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="水印设置" name="4">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="watermarkSwitch" class="ml-3" @change="canvasHandle('watermark')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">水印内容</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="waterMarkText" @change="canvasHandle('watermark')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" class="sheet" style="">
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-drawer__body){
|
||||
display: none !important;
|
||||
}
|
||||
:deep(.el-drawer__header){
|
||||
display: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
:deep(.el-drawer){
|
||||
height: 50% !important;
|
||||
}
|
||||
:deep(.el-upload-list){
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,487 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, onMounted } from 'vue'
|
||||
import Spreadsheet from 'x-data-spreadsheet'
|
||||
import 'x-data-spreadsheet/dist/locale/zh-cn';
|
||||
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
import { toEchartsPieData, toSpreadsheetData, tranObjAndColumn } from '@/utils/echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import * as XLSX from 'xlsx'
|
||||
const info = reactive({
|
||||
title: "饼图",
|
||||
})
|
||||
|
||||
const chartDom = ref<HTMLElement|null>()
|
||||
const myChart = ref<echarts.ECharts>()
|
||||
const dataFileRef = ref()
|
||||
|
||||
const setOptionName = ref(1)
|
||||
//缩放比例
|
||||
const sacleSize = ref(100)
|
||||
//画布宽高
|
||||
const widthCanvas = ref(720)
|
||||
const heightCanvas = ref(400)
|
||||
//下载文件类型
|
||||
const downType = ref('1')
|
||||
//图形属性颜色
|
||||
const attrColor = ref('#5470c6')
|
||||
//标题位置
|
||||
const titlePos = ref('center')
|
||||
//标题
|
||||
const title = ref('然部落在线工具')
|
||||
//副标题
|
||||
const subTitle = ref('在线图表制作工具')
|
||||
//显示标题 - 开关
|
||||
const titleSwitch = ref(true)
|
||||
//显示副标题 - 开关
|
||||
const subTitleSwitch = ref(true)
|
||||
|
||||
/** 水印 */
|
||||
const watermarkSwitch = ref(false) //开关
|
||||
const waterMarkText = ref('然部落工具');
|
||||
|
||||
//创建水印
|
||||
const createWatermark = () => {
|
||||
let canvas = document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
canvas.width = canvas.height = 100;
|
||||
ctx!.textAlign = 'center';
|
||||
ctx!.textBaseline = 'middle';
|
||||
ctx!.globalAlpha = 0.08;
|
||||
ctx!.font = '20px Microsoft Yahei';
|
||||
ctx!.translate(50, 50);
|
||||
ctx!.rotate(-Math.PI / 4);
|
||||
ctx!.fillText(waterMarkText.value, 0, 0);
|
||||
return canvas
|
||||
}
|
||||
|
||||
//操作图表
|
||||
const canvasHandle = (type) => {
|
||||
// let element = document.getElementById('main')
|
||||
let element = chartDom.value
|
||||
switch(type) {
|
||||
case "scale":
|
||||
//缩放画布
|
||||
let scale = sacleSize.value / 100
|
||||
element!.style.transform = `scale(${scale})`
|
||||
break
|
||||
case "size":
|
||||
//图表尺寸
|
||||
element!.style.width = widthCanvas.value + 'px';
|
||||
element!.style.height = heightCanvas.value + 'px';
|
||||
reloadCanvas()
|
||||
break
|
||||
case "title":
|
||||
let fakerTitle = ''
|
||||
let fakerSubTitle = ''
|
||||
//修改标题相关配置
|
||||
if (titleSwitch.value === true) {
|
||||
fakerTitle = title.value
|
||||
}
|
||||
if (subTitleSwitch.value === true) {
|
||||
fakerSubTitle = subTitle.value
|
||||
}
|
||||
myChart.value?.setOption({
|
||||
title: {
|
||||
text: fakerTitle,
|
||||
subtext: fakerSubTitle,
|
||||
left: titlePos.value
|
||||
}
|
||||
})
|
||||
break
|
||||
case "color":
|
||||
//图表属性颜色
|
||||
myChart.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
itemStyle: {
|
||||
color: attrColor.value
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
case "watermark":
|
||||
//水印
|
||||
if (watermarkSwitch.value === true) {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: {
|
||||
image: createWatermark(),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: '#fff'
|
||||
})
|
||||
}
|
||||
break;
|
||||
case "data":
|
||||
//更新数据
|
||||
myChart.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
data: seriesData.value,
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
const handleChange = () => {
|
||||
|
||||
}
|
||||
|
||||
//数据
|
||||
const colunmData = ref(['Search Engine', 'Direct', 'Email', 'Union Ads', 'Video Ads'] as string[]);
|
||||
const valueData = ref([1048, 735, 580, 484, 300] as number[]);
|
||||
const seriesData = ref([] as Object[])
|
||||
//选项
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: {
|
||||
// // type: 'pattern',
|
||||
// // image: canvas,
|
||||
// // repeat: 'repeat'
|
||||
// },
|
||||
title: {
|
||||
text: title.value,
|
||||
subtext: subTitle.value,
|
||||
left: titlePos.value
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: seriesData.value,
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
tooltip: {
|
||||
// trigger: 'item'
|
||||
},
|
||||
};
|
||||
|
||||
//重新加载画布
|
||||
const reloadCanvas = () => {
|
||||
myChart.value = echarts.init(chartDom.value)
|
||||
myChart.value.clear();
|
||||
myChart.value.resize({
|
||||
width: widthCanvas.value,
|
||||
height: heightCanvas.value
|
||||
})
|
||||
myChart.value.setOption(option)
|
||||
//加载数据
|
||||
canvasHandle('data')
|
||||
}
|
||||
|
||||
//下载echarts图表图片
|
||||
const downEchartsImg = () => {
|
||||
let ext = downType.value == '1' ? 'png' : 'jpeg'
|
||||
let imgUrl = myChart.value?.getDataURL({
|
||||
type: ext as "png" | 'jpeg',
|
||||
pixelRatio: 2,
|
||||
})
|
||||
|
||||
if (imgUrl != undefined) {
|
||||
var downloadLink = document.createElement('a');
|
||||
downloadLink.href = imgUrl;
|
||||
downloadLink.download = 'echart.' + ext;
|
||||
downloadLink.click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 上传、编辑数据 */
|
||||
//抽屉开关
|
||||
const drawer = ref(false)
|
||||
const rowsData = ref({})
|
||||
|
||||
const editData = () => {
|
||||
if (drawer.value == true) {
|
||||
drawer.value = false
|
||||
} else {
|
||||
drawer.value = true
|
||||
/** 在线excel */
|
||||
Spreadsheet.locale('zh-cn', (window.x_spreadsheet as any).$messages['zh-cn']);
|
||||
//const sheet = new Spreadsheet("#x-spreadsheet", {
|
||||
new Spreadsheet("#x-spreadsheet", {
|
||||
showToolbar: false, //隐藏顶部工具栏
|
||||
showBottomBar: false,//隐藏底部工具栏,
|
||||
view: {
|
||||
height: () => document.documentElement.clientHeight / 2,
|
||||
width: () => document.documentElement.clientWidth,
|
||||
}
|
||||
})
|
||||
.loadData({
|
||||
//样式
|
||||
styles: [
|
||||
{
|
||||
bgcolor: '#f4f5f8',
|
||||
textwrap: true,
|
||||
color: '#900b09',
|
||||
border: {
|
||||
top: ['thin', '#0366d6'],
|
||||
bottom: ['thin', '#0366d6'],
|
||||
right: ['thin', '#0366d6'],
|
||||
left: ['thin', '#0366d6'],
|
||||
},
|
||||
},
|
||||
],
|
||||
//数据
|
||||
rows: rowsData.value
|
||||
}) // load data
|
||||
.change(data => {
|
||||
//表格数据改变后触发
|
||||
//规则: 获取第一列和第二列的数据
|
||||
seriesData.value = toEchartsPieData(data)
|
||||
canvasHandle('data')
|
||||
});
|
||||
// sheet.validate()
|
||||
}
|
||||
}
|
||||
|
||||
//上传数据文件
|
||||
const fileList = ref()
|
||||
const updateDataFile = async (params) => {
|
||||
const _file = params.file;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = (ev) => {
|
||||
try {
|
||||
if (!ev.target) {
|
||||
return
|
||||
}
|
||||
const data = ev.target.result;
|
||||
const workbook = XLSX.read(data, {
|
||||
type: 'binary'
|
||||
});
|
||||
let useCount = 0;
|
||||
let tmpColumnData:any[] = [];
|
||||
let tmpValueData:any[] = [];
|
||||
for (let sheet in workbook.Sheets) {
|
||||
//这里只需要第一个sheet
|
||||
if (useCount > 0) {
|
||||
continue;
|
||||
}
|
||||
//循环读取每个文件
|
||||
const sheetArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {header: ['0', '1']});
|
||||
//若当前sheet没有数据,则continue
|
||||
if(sheetArray.length == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
for(let item in sheetArray){
|
||||
if (sheetArray[item] != undefined) {
|
||||
let tmp1 = sheetArray as []
|
||||
tmpColumnData.push(tmp1[item][0])
|
||||
tmpValueData.push(tmp1[item][1])
|
||||
}
|
||||
}
|
||||
useCount++
|
||||
}
|
||||
//更新数据
|
||||
colunmData.value = tmpColumnData
|
||||
valueData.value = tmpValueData
|
||||
seriesData.value = tranObjAndColumn([
|
||||
colunmData.value, valueData.value
|
||||
])
|
||||
//更新图表
|
||||
canvasHandle('data')
|
||||
//更新表格
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
} catch (e) {
|
||||
console.log('error')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
fileReader.readAsArrayBuffer(_file)
|
||||
}
|
||||
|
||||
//当超出限制时,执行的钩子函数
|
||||
//这里覆盖前一个文件
|
||||
const handleExceed: UploadProps['onExceed'] = (files) => {
|
||||
dataFileRef.value!.clearFiles()
|
||||
const file = files[0] as UploadRawFile
|
||||
file.uid = genFileId()
|
||||
dataFileRef.value!.handleStart(file)
|
||||
dataFileRef.value!.submit()
|
||||
}
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
//初始化数据
|
||||
seriesData.value = tranObjAndColumn([
|
||||
colunmData.value, valueData.value
|
||||
])
|
||||
//init echart dom
|
||||
chartDom.value = document.getElementById('main')
|
||||
//设置画布宽高
|
||||
canvasHandle('size')
|
||||
//reload canvas
|
||||
reloadCanvas()
|
||||
//数据格式转换成
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
// console.log(rowsData.value)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl flex">
|
||||
<div class="w-4/6">
|
||||
<div class="flex flex-row-reverse mb-4">
|
||||
<el-input-number v-model="sacleSize" :min="1" :max="100" :step="5" @change="canvasHandle('scale')" step-strictly/>
|
||||
<el-text>缩放:</el-text>
|
||||
</div>
|
||||
<!-- <div class="flex justify-center items-center"> -->
|
||||
<div class="flex justify-center items-center max-h-[500px] max-w-[1000px] overflow-auto">
|
||||
<div id="main" class="bg-white"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-2/6 bg-white ml-3 rounded-lg p-3">
|
||||
<div class="mb-3">
|
||||
<el-button class="w-full mb-3" type="primary" size="large" @click="downEchartsImg">下载图表</el-button>
|
||||
<div class="flex items-center ml-1">
|
||||
<el-text>文件类型</el-text>
|
||||
<el-radio-group v-model="downType" class="ml-4">
|
||||
<el-radio size="large" label="1">PNG</el-radio>
|
||||
<el-radio size="large" label="2">JPEG</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="支持上传:xls, xlsx, csv文件"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button class="w-1/2">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
class="dataFileRef flex"
|
||||
ref="dataFileRef"
|
||||
accept=".xls,.xlsx,.csv"
|
||||
:http-request="updateDataFile"
|
||||
:on-exceed="handleExceed"
|
||||
:limit="1"
|
||||
>
|
||||
上传数据
|
||||
</el-upload>
|
||||
</el-button>
|
||||
|
||||
</el-tooltip>
|
||||
<el-button class="w-1/2" @click="editData">编辑数据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse v-model="setOptionName" @change="handleChange" accordion>
|
||||
<el-collapse-item title="画布设置" name="1">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">宽度</el-text>
|
||||
<el-input-number
|
||||
v-model="widthCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex mt-3">
|
||||
<el-text class="w-16 text-right">高度</el-text>
|
||||
<el-input-number
|
||||
v-model="heightCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="标题设置" name="2">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">位置</el-text>
|
||||
<el-radio-group v-model="titlePos" class="ml-3" @change="canvasHandle('title')">
|
||||
<el-radio-button label="left" value="left">左</el-radio-button>
|
||||
<el-radio-button label="center" value="center">中</el-radio-button>
|
||||
<el-radio-button label="right" value="right">右</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-divider content-position="left" class="pl-10">标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="titleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="title" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
<el-divider content-position="left">副标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="subTitleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">副标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="subTitle" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<!-- <el-collapse-item title="图形属性" name="3">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">颜色</el-text>
|
||||
<el-color-picker v-model="attrColor" class="ml-3" @change="canvasHandle('color')"/>
|
||||
</div>
|
||||
</el-collapse-item> -->
|
||||
<el-collapse-item title="水印设置" name="4">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="watermarkSwitch" class="ml-3" @change="canvasHandle('watermark')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">水印内容</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="waterMarkText" @change="canvasHandle('watermark')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" custom-class="sheet" style="">
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-drawer__body){
|
||||
display: none !important;
|
||||
}
|
||||
:deep(.el-drawer__header){
|
||||
display: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
:deep(.el-drawer){
|
||||
height: 50% !important;
|
||||
}
|
||||
:deep(.el-upload-list){
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,482 @@
|
||||
<script setup lang="ts">
|
||||
import {ref, reactive, onMounted } from 'vue'
|
||||
import Spreadsheet from 'x-data-spreadsheet'
|
||||
import 'x-data-spreadsheet/dist/locale/zh-cn';
|
||||
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
import { toEchartsData, toSpreadsheetData, tranObjAndColumn } from '@/utils/echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import * as XLSX from 'xlsx'
|
||||
const info = reactive({
|
||||
title: "散点图",
|
||||
})
|
||||
|
||||
const chartDom = ref<HTMLElement|null>()
|
||||
const myChart = ref<echarts.ECharts>()
|
||||
const dataFileRef = ref()
|
||||
|
||||
const setOptionName = ref(1)
|
||||
//缩放比例
|
||||
const sacleSize = ref(100)
|
||||
//画布宽高
|
||||
const widthCanvas = ref(720)
|
||||
const heightCanvas = ref(400)
|
||||
//下载文件类型
|
||||
const downType = ref('1')
|
||||
//图形属性颜色
|
||||
const attrColor = ref('#5470c6')
|
||||
//标题位置
|
||||
const titlePos = ref('center')
|
||||
//标题
|
||||
const title = ref('然部落在线工具')
|
||||
//副标题
|
||||
const subTitle = ref('在线图表制作工具')
|
||||
//显示标题 - 开关
|
||||
const titleSwitch = ref(true)
|
||||
//显示副标题 - 开关
|
||||
const subTitleSwitch = ref(true)
|
||||
|
||||
/** 水印 */
|
||||
const watermarkSwitch = ref(false) //开关
|
||||
const waterMarkText = ref('然部落工具');
|
||||
|
||||
//创建水印
|
||||
const createWatermark = () => {
|
||||
let canvas = document.createElement('canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
canvas.width = canvas.height = 100;
|
||||
ctx!.textAlign = 'center';
|
||||
ctx!.textBaseline = 'middle';
|
||||
ctx!.globalAlpha = 0.08;
|
||||
ctx!.font = '20px Microsoft Yahei';
|
||||
ctx!.translate(50, 50);
|
||||
ctx!.rotate(-Math.PI / 4);
|
||||
ctx!.fillText(waterMarkText.value, 0, 0);
|
||||
return canvas
|
||||
}
|
||||
|
||||
//操作图表
|
||||
const canvasHandle = (type) => {
|
||||
// let element = document.getElementById('main')
|
||||
let element = chartDom.value
|
||||
switch(type) {
|
||||
case "scale":
|
||||
//缩放画布
|
||||
let scale = sacleSize.value / 100
|
||||
element!.style.transform = `scale(${scale})`
|
||||
break
|
||||
case "size":
|
||||
//图表尺寸
|
||||
element!.style.width = widthCanvas.value + 'px';
|
||||
element!.style.height = heightCanvas.value + 'px';
|
||||
reloadCanvas()
|
||||
break
|
||||
case "title":
|
||||
let fakerTitle = ''
|
||||
let fakerSubTitle = ''
|
||||
//修改标题相关配置
|
||||
if (titleSwitch.value === true) {
|
||||
fakerTitle = title.value
|
||||
}
|
||||
if (subTitleSwitch.value === true) {
|
||||
fakerSubTitle = subTitle.value
|
||||
}
|
||||
myChart.value?.setOption({
|
||||
title: {
|
||||
text: fakerTitle,
|
||||
subtext: fakerSubTitle,
|
||||
left: titlePos.value
|
||||
}
|
||||
})
|
||||
break
|
||||
case "color":
|
||||
//图表属性颜色
|
||||
myChart.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
itemStyle: {
|
||||
color: attrColor.value
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
case "watermark":
|
||||
//水印
|
||||
if (watermarkSwitch.value === true) {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: {
|
||||
image: createWatermark(),
|
||||
}
|
||||
})
|
||||
} else {
|
||||
myChart.value?.setOption({
|
||||
backgroundColor: '#fff'
|
||||
})
|
||||
}
|
||||
break;
|
||||
case "data":
|
||||
//更新数据
|
||||
myChart.value?.setOption({
|
||||
series: [
|
||||
{
|
||||
data: seriesData.value,
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
}
|
||||
}
|
||||
const handleChange = () => {
|
||||
|
||||
}
|
||||
|
||||
//数据
|
||||
const colunmData = ref([10.0, 8.07, 13.0, 9.05, 11.0, 14.0, 13.4, 10.0, 14.0, 12.5, 9.15, 11.5, 3.03, 12.2, 2.02, 1.05, 4.05, 6.03, 12.0, 7.08, 5.02, 6.03]);
|
||||
const valueData = ref([8.04, 6.95, 7.58, 8.81, 8.33, 7.66, 6.81, 6.33, 8.96, 6.82, 7.2, 7.2, 4.23, 7.83, 4.47, 3.33, 4.96, 7.24, 6.26, 8.84, 5.82, 5.68, 7.05]);
|
||||
const seriesData = ref([] as Array<any>[])
|
||||
//选项
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
// backgroundColor: {
|
||||
// // type: 'pattern',
|
||||
// // image: canvas,
|
||||
// // repeat: 'repeat'
|
||||
// },
|
||||
title: {
|
||||
text: title.value,
|
||||
subtext: subTitle.value,
|
||||
left: titlePos.value
|
||||
},
|
||||
xAxis: {},
|
||||
yAxis: {},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
symbolSize: 30,
|
||||
data: seriesData.value,
|
||||
}
|
||||
],
|
||||
tooltip: {}
|
||||
};
|
||||
|
||||
//重新加载画布
|
||||
const reloadCanvas = () => {
|
||||
myChart.value = echarts.init(chartDom.value)
|
||||
myChart.value.clear();
|
||||
myChart.value.resize({
|
||||
width: widthCanvas.value,
|
||||
height: heightCanvas.value
|
||||
})
|
||||
myChart.value.setOption(option)
|
||||
//加载数据
|
||||
canvasHandle('data')
|
||||
}
|
||||
|
||||
//下载echarts图表图片
|
||||
const downEchartsImg = () => {
|
||||
let ext = downType.value == '1' ? 'png' : 'jpeg'
|
||||
let imgUrl = myChart.value?.getDataURL({
|
||||
type: ext as "png" | 'jpeg',
|
||||
pixelRatio: 2,
|
||||
})
|
||||
|
||||
if (imgUrl != undefined) {
|
||||
var downloadLink = document.createElement('a');
|
||||
downloadLink.href = imgUrl;
|
||||
downloadLink.download = 'echart.' + ext;
|
||||
downloadLink.click();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 上传、编辑数据 */
|
||||
//抽屉开关
|
||||
const drawer = ref(false)
|
||||
const rowsData = ref({})
|
||||
|
||||
const editData = () => {
|
||||
if (drawer.value == true) {
|
||||
drawer.value = false
|
||||
} else {
|
||||
drawer.value = true
|
||||
/** 在线excel */
|
||||
Spreadsheet.locale('zh-cn', (window.x_spreadsheet as any).$messages['zh-cn']);
|
||||
//const sheet = new Spreadsheet("#x-spreadsheet", {
|
||||
new Spreadsheet("#x-spreadsheet", {
|
||||
showToolbar: false, //隐藏顶部工具栏
|
||||
showBottomBar: false,//隐藏底部工具栏,
|
||||
view: {
|
||||
height: () => document.documentElement.clientHeight / 2,
|
||||
width: () => document.documentElement.clientWidth,
|
||||
}
|
||||
})
|
||||
.loadData({
|
||||
//样式
|
||||
styles: [
|
||||
{
|
||||
bgcolor: '#f4f5f8',
|
||||
textwrap: true,
|
||||
color: '#900b09',
|
||||
border: {
|
||||
top: ['thin', '#0366d6'],
|
||||
bottom: ['thin', '#0366d6'],
|
||||
right: ['thin', '#0366d6'],
|
||||
left: ['thin', '#0366d6'],
|
||||
},
|
||||
},
|
||||
],
|
||||
//数据
|
||||
rows: rowsData.value
|
||||
}) // load data
|
||||
.change(data => {
|
||||
//表格数据改变后触发
|
||||
//规则: 获取第一列和第二列的数据
|
||||
let tmp = toEchartsData(data)
|
||||
colunmData.value = tmp[0]
|
||||
valueData.value = tmp[1]
|
||||
canvasHandle('data')
|
||||
});
|
||||
// sheet.validate()
|
||||
}
|
||||
}
|
||||
|
||||
//上传数据文件
|
||||
const fileList = ref()
|
||||
const updateDataFile = async (params) => {
|
||||
const _file = params.file;
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = (ev) => {
|
||||
try {
|
||||
if (!ev.target) {
|
||||
return
|
||||
}
|
||||
const data = ev.target.result;
|
||||
const workbook = XLSX.read(data, {
|
||||
type: 'binary'
|
||||
});
|
||||
let useCount = 0;
|
||||
let tmpColumnData:any[] = [];
|
||||
let tmpValueData:any[] = [];
|
||||
for (let sheet in workbook.Sheets) {
|
||||
//这里只需要第一个sheet
|
||||
if (useCount > 0) {
|
||||
continue;
|
||||
}
|
||||
//循环读取每个文件
|
||||
const sheetArray = XLSX.utils.sheet_to_json(workbook.Sheets[sheet], {header: ['0', '1']});
|
||||
//若当前sheet没有数据,则continue
|
||||
if(sheetArray.length == 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
for(let item in sheetArray){
|
||||
if (sheetArray[item] != undefined) {
|
||||
let tmp1 = sheetArray as []
|
||||
tmpColumnData.push(tmp1[item][0])
|
||||
tmpValueData.push(tmp1[item][1])
|
||||
}
|
||||
}
|
||||
useCount++
|
||||
}
|
||||
//更新数据
|
||||
colunmData.value = tmpColumnData
|
||||
valueData.value = tmpValueData
|
||||
seriesData.value = tranObjAndColumn([
|
||||
colunmData.value, valueData.value
|
||||
])
|
||||
//更新图表
|
||||
canvasHandle('data')
|
||||
//更新表格
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
} catch (e) {
|
||||
console.log('error')
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
fileReader.readAsArrayBuffer(_file)
|
||||
}
|
||||
|
||||
//当超出限制时,执行的钩子函数
|
||||
//这里覆盖前一个文件
|
||||
const handleExceed: UploadProps['onExceed'] = (files) => {
|
||||
dataFileRef.value!.clearFiles()
|
||||
const file = files[0] as UploadRawFile
|
||||
file.uid = genFileId()
|
||||
dataFileRef.value!.handleStart(file)
|
||||
dataFileRef.value!.submit()
|
||||
}
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
|
||||
onMounted(() => {
|
||||
//初始化数据
|
||||
seriesData.value = tranObjAndColumn([
|
||||
colunmData.value, valueData.value
|
||||
], 'toCoord')
|
||||
//init echart dom
|
||||
chartDom.value = document.getElementById('main')
|
||||
//设置画布宽高
|
||||
canvasHandle('size')
|
||||
//reload canvas
|
||||
reloadCanvas()
|
||||
//数据格式转换成
|
||||
rowsData.value = toSpreadsheetData([
|
||||
colunmData, valueData
|
||||
])
|
||||
// console.log(rowsData.value)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl flex">
|
||||
<div class="w-4/6">
|
||||
<div class="flex flex-row-reverse mb-4">
|
||||
<el-input-number v-model="sacleSize" :min="1" :max="100" :step="5" @change="canvasHandle('scale')" step-strictly/>
|
||||
<el-text>缩放:</el-text>
|
||||
</div>
|
||||
<!-- <div class="flex justify-center items-center"> -->
|
||||
<div class="flex justify-center items-center max-h-[500px] max-w-[1000px] overflow-auto">
|
||||
<div id="main" class="bg-white"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-2/6 bg-white ml-3 rounded-lg p-3">
|
||||
<div class="mb-3">
|
||||
<el-button class="w-full mb-3" type="primary" size="large" @click="downEchartsImg">下载图表</el-button>
|
||||
<div class="flex items-center ml-1">
|
||||
<el-text>文件类型</el-text>
|
||||
<el-radio-group v-model="downType" class="ml-4">
|
||||
<el-radio size="large" label="1">PNG</el-radio>
|
||||
<el-radio size="large" label="2">JPEG</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
content="支持上传:xls, xlsx, csv文件"
|
||||
placement="top-start"
|
||||
>
|
||||
<el-button class="w-1/2">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
class="dataFileRef flex"
|
||||
ref="dataFileRef"
|
||||
accept=".xls,.xlsx,.csv"
|
||||
:http-request="updateDataFile"
|
||||
:on-exceed="handleExceed"
|
||||
:limit="1"
|
||||
>
|
||||
上传数据
|
||||
</el-upload>
|
||||
</el-button>
|
||||
|
||||
</el-tooltip>
|
||||
<el-button class="w-1/2" @click="editData">编辑数据</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-collapse v-model="setOptionName" @change="handleChange" accordion>
|
||||
<el-collapse-item title="画布设置" name="1">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">宽度</el-text>
|
||||
<el-input-number
|
||||
v-model="widthCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex mt-3">
|
||||
<el-text class="w-16 text-right">高度</el-text>
|
||||
<el-input-number
|
||||
v-model="heightCanvas"
|
||||
:min="1"
|
||||
:max="4000"
|
||||
:step="10"
|
||||
controls-position="right"
|
||||
class="h-8 w-60 max-w-[60%] ml-3"
|
||||
@change="canvasHandle('size')"
|
||||
/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="标题设置" name="2">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">位置</el-text>
|
||||
<el-radio-group v-model="titlePos" class="ml-3" @change="canvasHandle('title')">
|
||||
<el-radio-button label="left" value="left">左</el-radio-button>
|
||||
<el-radio-button label="center" value="center">中</el-radio-button>
|
||||
<el-radio-button label="right" value="right">右</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-divider content-position="left" class="pl-10">标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="titleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="title" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
<el-divider content-position="left">副标题</el-divider>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="subTitleSwitch" class="ml-3" @change="canvasHandle('title')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">副标题</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="subTitle" @blur="canvasHandle('title')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="图形属性" name="3">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">颜色</el-text>
|
||||
<el-color-picker v-model="attrColor" class="ml-3" @change="canvasHandle('color')"/>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="水印设置" name="4">
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">显示</el-text>
|
||||
<el-switch v-model="watermarkSwitch" class="ml-3" @change="canvasHandle('watermark')"/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<el-text class="w-16 text-right">水印内容</el-text>
|
||||
<el-input class="h-8 w-60 max-w-[60%] ml-3" v-model="waterMarkText" @change="canvasHandle('watermark')"></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-drawer id="x-spreadsheet" v-model="drawer" direction="btt" custom-class="sheet" style="">
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-drawer__body){
|
||||
display: none !important;
|
||||
}
|
||||
:deep(.el-drawer__header){
|
||||
display: none !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
:deep(.el-drawer){
|
||||
height: 50% !important;
|
||||
}
|
||||
:deep(.el-upload-list){
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,133 @@
|
||||
<script setup lang="ts">
|
||||
import { ref,reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "抛硬币",
|
||||
})
|
||||
|
||||
const genStatus = ref(false)
|
||||
const isHeads = ref(true)
|
||||
|
||||
const throwCoin = () => {
|
||||
genStatus.value = true
|
||||
isHeads.value = Math.random() < 0.5;
|
||||
setTimeout(() => {
|
||||
genStatus.value = false;
|
||||
}, 2500); // 休眠5秒,5000毫秒
|
||||
}
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div style="flex-grow: 3; display: flex; justify-content: center;">
|
||||
<!-- coin -->
|
||||
<div class="rotate-wrap" :class="[genStatus == true ? (isHeads == true ? 'rotate-wrap-ani-z' : 'rotate-wrap-ani-f') : '']">
|
||||
<div class="front circle" :class="[isHeads == true ? 'is-head' : '']"></div>
|
||||
<div class="reverse circle" :class="[isHeads == false ? 'is-head' : '']"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<el-button type="primary" class="w-48" size="large" @click="throwCoin" v-if="!genStatus">抛硬币</el-button>
|
||||
<el-button type="primary" class="w-48" size="large" disabled v-else>抛硬币...</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.rotate-wrap {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin: 100px;
|
||||
transform-style: preserve-3d;
|
||||
position: relative;
|
||||
}
|
||||
.rotate-wrap-ani-z{
|
||||
/* animation: rotate 5s; */
|
||||
animation: rotateZ 2.5s linear infinite;
|
||||
}
|
||||
.rotate-wrap-ani-f{
|
||||
/* animation: rotate 5s; */
|
||||
animation: rotateF 2.5s linear infinite;
|
||||
}
|
||||
.rotate-wrap:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.rotate-wrap .front, .rotate-wrap .reverse{
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-size: cover;
|
||||
}
|
||||
.rotate-wrap .front{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-image: url('/public/images/coin/dollar.png');
|
||||
}
|
||||
.rotate-wrap .reverse{
|
||||
background-image: url('/public/images/coin/xingxing.png');
|
||||
}
|
||||
|
||||
.circle{
|
||||
border-radius: 50%;
|
||||
}
|
||||
.line{
|
||||
height: 200px;
|
||||
width: 1px;
|
||||
position:absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
background: red;
|
||||
transform: translateZ(0px);
|
||||
}
|
||||
.is-head{
|
||||
transform: translateZ(1px);
|
||||
}
|
||||
|
||||
@keyframes rotateZ {
|
||||
0%{
|
||||
transform: rotateX(0deg);
|
||||
}
|
||||
20%{
|
||||
transform: rotateX(720deg) scale(1.2);
|
||||
}
|
||||
50%{
|
||||
transform: rotateX(1080deg) scale(1.5);
|
||||
}
|
||||
80%{
|
||||
transform: rotateX(1440deg) scale(1.2);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(2160deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateF {
|
||||
0%{
|
||||
transform: rotateX(0deg);
|
||||
}
|
||||
20%{
|
||||
transform: rotateX(720deg) scale(1.2);
|
||||
}
|
||||
50%{
|
||||
transform: rotateX(1080deg) scale(1.5);
|
||||
}
|
||||
80%{
|
||||
transform: rotateX(1440deg) scale(1.2);
|
||||
}
|
||||
100% {
|
||||
transform: rotateX(1980deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,94 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, onMounted,ref } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "帮我决定",
|
||||
scope: '', //多个用`,`隔开
|
||||
randomVal: '',
|
||||
checked: '0',
|
||||
food: '沙县,烧腊,小炒肉,兰州拉面,汤饭,黄焖鸡,卤肉饭,蛋炒饭,羊肉粉,螺蛳粉,猪脚饭,白切鸡,肯德基,火锅,烧烤,泡面,西北风,煎饼果子',
|
||||
subject: '电影,听音乐,游戏,逛街,看电视,睡觉,吃饭,清洁,锻炼,学习,发呆,散步,购物,回家,唱歌',
|
||||
})
|
||||
|
||||
const chooseStatus = ref(false)
|
||||
|
||||
const changeCheckBox = (val: any) => {
|
||||
switch(val) {
|
||||
case '0':
|
||||
info.scope = info.food
|
||||
break;
|
||||
case '1':
|
||||
info.scope = info.subject
|
||||
break;
|
||||
default:
|
||||
info.scope = ''
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const init = () => {
|
||||
changeCheckBox(info.checked)
|
||||
}
|
||||
|
||||
//选择
|
||||
const choose = () => {
|
||||
chooseStatus.value = true
|
||||
let count = 0
|
||||
let scopeList = info.scope.split(',')
|
||||
const chooseInterval = setInterval(() => {
|
||||
const randomNum = Math.floor(Math.random() * scopeList.length);
|
||||
info.randomVal = scopeList[randomNum]
|
||||
count++
|
||||
if (count > 10) {
|
||||
clearTimeout(chooseInterval)
|
||||
chooseStatus.value = false
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
info.scope = ''
|
||||
info.checked = ''
|
||||
info.randomVal = ''
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-radio-group v-model="info.checked" @change="changeCheckBox">
|
||||
<el-radio label="0" size="large">吃什么</el-radio>
|
||||
<el-radio label="1" size="large">干什么</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-input type="textarea" v-model="info.scope" :rows="5"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="text-5xl h-24 leading-[6rem]" v-if="info.randomVal != ''">
|
||||
{{ info.randomVal }}
|
||||
<!-- <el-text class=""></el-text> -->
|
||||
</div>
|
||||
|
||||
<div class="mt-3">
|
||||
<el-button @click="choose" v-if="!chooseStatus" type="primary">帮我决定</el-button>
|
||||
<el-button @click="choose" type="primary" disabled v-else>决定中...</el-button>
|
||||
<el-button @click="clear">清空</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
import { ref,reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import DiceCore from "@/components/Tools/Dice/DiceCore.vue"
|
||||
// import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "投骰子",
|
||||
})
|
||||
//骰子数量
|
||||
const diceNum = ref(1)
|
||||
const genStatus = ref(false)
|
||||
const diceRef = ref()
|
||||
const resetDiceKey = ref(0)
|
||||
|
||||
const shake = async () => {
|
||||
genStatus.value = true
|
||||
|
||||
let res = await new Promise((resolve) => {
|
||||
for (let i = 0; i < diceNum.value; i++) {
|
||||
diceRef.value[i].throwDice()
|
||||
}
|
||||
resolve(true)
|
||||
})
|
||||
|
||||
//等待执行完成
|
||||
setTimeout(() => {
|
||||
console.log(res)
|
||||
genStatus.value = false
|
||||
}, 1200)
|
||||
}
|
||||
|
||||
//重置骰子点数
|
||||
const resetDice = () => {
|
||||
resetDiceKey.value++
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<view class="grow-[1.5]"></view>
|
||||
<div class="flex justify-center grow-[3]">
|
||||
<div class="flex flex-wrap h-96 items-center">
|
||||
<!-- 骰子组件 -->
|
||||
<DiceCore ref="diceRef" v-for="i in diceNum" :key="resetDiceKey" :class="[diceNum == 1 ? 'dice_full' : '', diceNum == 3 && i == 1 ? 'dice_full' : 'dice_default', diceNum == 5 && i == 3 ? 'dice_full' : 'dice_default']"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="ml-5 flex justify-center">
|
||||
<el-text style="">骰子数量:</el-text>
|
||||
<div class="w-4/6">
|
||||
<el-slider class="w-72 ml-3" v-model="diceNum" :min="1" :max="6" :step="1" blockSize="18" :showValue="true"></el-slider>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-5 flex justify-center">
|
||||
<el-button type="primary" class="w-48" size="large" @click="shake" v-if="!genStatus">投骰子</el-button>
|
||||
<el-button type="primary" class="w-48" size="large" disabled v-else>投骰子...</el-button>
|
||||
<el-button type="primary" class="w-48" size="large" @click="resetDice">重置</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dice_default{
|
||||
flex: 0 0 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.dice_full{
|
||||
flex: 0 0 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,115 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onBeforeUnmount } from 'vue'
|
||||
|
||||
//定义emit
|
||||
const emit = defineEmits(['throwEnd'])
|
||||
//是否正在掷筛子
|
||||
const isDicing = ref(false)
|
||||
//当前显示的动画图片索引
|
||||
const aniIndex = ref(0)
|
||||
//掷筛子动画所用到的图片(4张骰子旋转过程中的图)
|
||||
const diceAnimationImages = ref([
|
||||
'/public/images/dice/dice_run1.png',
|
||||
'/public/images/dice/dice_run2.png',
|
||||
'/public/images/dice/dice_run3.png',
|
||||
'/public/images/dice/dice_run4.png'
|
||||
])
|
||||
//骰子每一个点的对应的图片
|
||||
const diceImages = ref({
|
||||
//1点的图片
|
||||
1: '/public/images/dice/dice1.png',
|
||||
//2点的图片
|
||||
2: '/public/images/dice/dice2.png',
|
||||
//3点的图片
|
||||
3: '/public/images/dice/dice3.png',
|
||||
//4点的图片
|
||||
4: '/public/images/dice/dice4.png',
|
||||
//5点的图片
|
||||
5: '/public/images/dice/dice5.png',
|
||||
//6点的图片
|
||||
6: '/public/images/dice/dice6.png'
|
||||
})
|
||||
//当前掷筛子掷到的点数(默认1点)
|
||||
const currentPoint = ref(1)
|
||||
//定时器
|
||||
const timer = ref(0)
|
||||
|
||||
const getDicePoint = async () => {
|
||||
let point = 1;
|
||||
//从1~6随机一个数
|
||||
point = Math.floor(Math.random() * 6 + 1);
|
||||
return point;
|
||||
}
|
||||
//掷骰子
|
||||
const throwDice = async () => {
|
||||
//如果当前骰子正在滚动则不能掷骰子
|
||||
if (isDicing.value) {
|
||||
return
|
||||
}
|
||||
//从接口获取点数
|
||||
currentPoint.value = await getDicePoint();
|
||||
//开启骰子动画
|
||||
await startAnimation();
|
||||
//动画完毕之后可以通知父组件当前掷到的点数
|
||||
emit('throwEnd', currentPoint.value);
|
||||
}
|
||||
//开启动画效果
|
||||
const startAnimation = async () => {
|
||||
return new Promise((resolve) => {
|
||||
//设置筛子开始运动
|
||||
isDicing.value = true;
|
||||
//记录动画次数
|
||||
let num = 0;
|
||||
//每隔100毫秒来回切换一张“动”图形成掷骰子的动画
|
||||
timer.value = setInterval(() => {
|
||||
let index = aniIndex.value;
|
||||
index++;
|
||||
if (index >= diceAnimationImages.value.length) {
|
||||
index = 0;
|
||||
}
|
||||
aniIndex.value = index;
|
||||
num++;
|
||||
//差不多执行1.2秒钟的时候可以停止了
|
||||
if (num > 12) {
|
||||
//关闭定时器
|
||||
clearInterval(timer.value);
|
||||
//设置骰子停止
|
||||
isDicing.value = false;
|
||||
//返回结果
|
||||
resolve(true);
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
//组件销毁之前清除定时器
|
||||
clearInterval(timer.value);
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
throwDice
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- 骰子组件 -->
|
||||
<div class="dice-wrap" @tap="throwDice">
|
||||
<!-- 筛子运动时候的展示的图片 -->
|
||||
<el-image v-if="isDicing" :src="diceAnimationImages[aniIndex]" class="dice-icon" loading="lazy"></el-image>
|
||||
<!-- 筛子静止时候的显示的对应点数的图片 -->
|
||||
<el-image v-else :src="diceImages[currentPoint]" class="dice-icon" loading="lazy"></el-image>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dice-wrap {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.dice-icon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,39 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { CodeDiff } from "v-code-diff";
|
||||
// import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "文本对比",
|
||||
leftTxt: '旧文本',
|
||||
rightTxt: '新文本'
|
||||
})
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="">
|
||||
<div class="flex justify-between">
|
||||
<el-input type="textarea" :rows="16" v-model="info.leftTxt"></el-input>
|
||||
<el-input type="textarea" :rows="16" v-model="info.rightTxt" class="ml-3"></el-input>
|
||||
</div>
|
||||
<code-diff
|
||||
:old-string="info.leftTxt"
|
||||
:new-string="info.rightTxt"
|
||||
output-format="side-by-side"
|
||||
theme="dark"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,27 @@
|
||||
<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: "tool name",
|
||||
})
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,143 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref,computed, watch } from 'vue'
|
||||
import { UploadProps,UploadRawFile,genFileId } from 'element-plus'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
|
||||
const info = reactive({
|
||||
title: "图片切割",
|
||||
})
|
||||
|
||||
const fileList = ref()
|
||||
const lineNum = ref(3)
|
||||
const image = ref({} as any)
|
||||
const cutImg = ref([] as string[])
|
||||
const dataFileRef = ref()
|
||||
|
||||
//上传
|
||||
const updateDataFile = async (params) => {
|
||||
let reader = new FileReader();
|
||||
reader.readAsDataURL(params.file);
|
||||
reader.addEventListener(
|
||||
'load',
|
||||
async () => {
|
||||
const imageTmp = new Image();
|
||||
imageTmp.onload = () => {
|
||||
image.value = imageTmp;
|
||||
cut();
|
||||
};
|
||||
imageTmp.src = reader.result as string;
|
||||
},
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
//当超出限制时,执行的钩子函数
|
||||
//这里覆盖前一个文件
|
||||
const handleExceed: UploadProps['onExceed'] = (files) => {
|
||||
dataFileRef.value!.clearFiles()
|
||||
const file = files[0] as UploadRawFile
|
||||
file.uid = genFileId()
|
||||
dataFileRef.value!.handleStart(file)
|
||||
dataFileRef.value!.submit()
|
||||
}
|
||||
|
||||
//切割
|
||||
const cut = () => {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const img = image.value;
|
||||
|
||||
const wpiece = Math.floor(img.naturalWidth / lineNum.value);
|
||||
const hpiece = Math.floor(img.naturalHeight / lineNum.value);
|
||||
const results = [] as string[];
|
||||
|
||||
canvas.width = wpiece;
|
||||
canvas.height = hpiece;
|
||||
|
||||
for (let r = 0; r < lineNum.value; r++) {
|
||||
for (let c = 0; c < lineNum.value; c++) {
|
||||
ctx?.drawImage(
|
||||
img,
|
||||
c * wpiece,
|
||||
r * hpiece,
|
||||
wpiece,
|
||||
hpiece,
|
||||
0,
|
||||
0,
|
||||
wpiece,
|
||||
hpiece
|
||||
);
|
||||
results.push(canvas.toDataURL());
|
||||
}
|
||||
}
|
||||
cutImg.value = results;
|
||||
}
|
||||
|
||||
//计算切割样式
|
||||
const cutImgStyle = computed(() => {
|
||||
const width = `width: ${image.value.width || 800}px;`;
|
||||
const grid = `grid: repeat(${lineNum.value}, 1fr) / repeat(${
|
||||
lineNum.value
|
||||
}, 1fr);`;
|
||||
return width + grid;
|
||||
})
|
||||
|
||||
watch(cutImgStyle, () => {
|
||||
if (fileList.value) cut();
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
class="dataFileRef flex"
|
||||
ref="dataFileRef"
|
||||
accept="image/*"
|
||||
:http-request="updateDataFile"
|
||||
:on-exceed="handleExceed"
|
||||
:limit="1"
|
||||
>
|
||||
<el-button type="primary">请上传需要切割的图片</el-button>
|
||||
</el-upload>
|
||||
<div class="flex mt-3">
|
||||
<div class="flex">
|
||||
<el-text>行数和列数</el-text>
|
||||
<el-input-number v-model="lineNum" :min="1" :max="10" class="ml-3"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex flex-col" v-if="image.src">
|
||||
<!-- 预览 -->
|
||||
<div class="mb-3">
|
||||
<el-text>预览: </el-text>
|
||||
<div :style="cutImgStyle" class="grid gap-1 max-w-full">
|
||||
<img v-for="(src,index) in cutImg" :key="index" :src="src" alt="结果" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 原图 -->
|
||||
<div>
|
||||
<el-text>原图: </el-text>
|
||||
<div>
|
||||
<img :src="image.src" alt="原图" v-if="image.src"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-empty :image-size="200" description="无预览"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive } from 'vue'
|
||||
import { useToolsStore } from '@/store/modules/tools'
|
||||
import { isIp } from '@/utils/verify'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
|
||||
const info = reactive({
|
||||
title: "IP查询",
|
||||
})
|
||||
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
const params = reactive({
|
||||
ip: '',
|
||||
})
|
||||
//查询
|
||||
const search = async (type: string) => {
|
||||
try {
|
||||
//通过点击获取ip信息
|
||||
if (type == 'click') {
|
||||
//验证是否ip地址
|
||||
if (!isIp(params.ip)) {
|
||||
ElMessage({
|
||||
message: "无效的ip地址",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
//获取信息
|
||||
await toolsStore.getIp(params)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
search('')
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="flex">
|
||||
<div class="mr-2 w-60">
|
||||
<el-input v-model="params.ip" ></el-input>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" @click="search('click')">查询</el-button>
|
||||
</div>
|
||||
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex flex-col">
|
||||
<div class="bg-white p-2 font-bold text-xl">您的IP信息</div>
|
||||
<div class="bg-white p-2 flex">
|
||||
<div class="mr-1">运营商: </div>
|
||||
<div>{{ toolsStore.ipData.isp }}</div>
|
||||
</div>
|
||||
<div class="bg-white p-2 flex">
|
||||
<div class="mr-1">域名解析地址: </div>
|
||||
<div>{{ toolsStore.ipData.ip }}</div>
|
||||
</div>
|
||||
<div class="bg-white p-2 flex">
|
||||
<div class="mr-1">所在地理位置: </div>
|
||||
<div>{{ toolsStore.ipData.pos }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { transferred, copy } from '@/utils/string';
|
||||
import { Codemirror } from "vue-codemirror";
|
||||
import { json } from '@codemirror/lang-json';
|
||||
import '@codemirror/search';
|
||||
import { lineNumbers } from '@codemirror/view';
|
||||
import '@codemirror/state';
|
||||
import '@codemirror/commands';
|
||||
// import { syntaxTree } from '@codemirror/language';
|
||||
// import { linter, Diagnostic } from '@codemirror/lint';
|
||||
|
||||
// const regexpLinter = linter(view => {
|
||||
// let diagnostics: Diagnostic[] = []
|
||||
// syntaxTree(view.state).cursor().iterate(node => {
|
||||
// if (node.name == "number") diagnostics.push({
|
||||
// from: node.from,
|
||||
// to: node.to,
|
||||
// severity: "warning",
|
||||
// message: "Regular expressions are FORBIDDEN",
|
||||
// actions: [{
|
||||
// name: "Remove",
|
||||
// apply(view, from, to) { view.dispatch({changes: {from, to}}) }
|
||||
// }]
|
||||
// })
|
||||
// })
|
||||
// return diagnostics
|
||||
// })
|
||||
|
||||
const info = reactive({
|
||||
title: "Json在线转换",
|
||||
code: '',
|
||||
extensions: [json(), lineNumbers()],
|
||||
isParseErr: false,
|
||||
parseErr: ''
|
||||
})
|
||||
|
||||
|
||||
//格式化json
|
||||
const formatJson = () => {
|
||||
try {
|
||||
//初始化错误信息
|
||||
info.isParseErr = false;
|
||||
info.parseErr = ''
|
||||
// 1、JSON.parse:把JSON字符串转换为JSON对象
|
||||
// 2、JSON.stringify:把JSON对象 转换为 有缩进的 JSON字符串格式
|
||||
info.code = JSON.stringify(JSON.parse(info.code), null, '\t');
|
||||
} catch(error) {
|
||||
info.isParseErr = true;
|
||||
// info.parseErr = (error as Error).message
|
||||
info.parseErr = '无效的JSON'
|
||||
}
|
||||
}
|
||||
|
||||
//压缩
|
||||
const compress = () => {
|
||||
info.code = info.code.replace(/[\r\n\t]/g, "")
|
||||
}
|
||||
|
||||
//转义
|
||||
const tran = () => {
|
||||
info.code = transferred(info.code, "\"")
|
||||
}
|
||||
|
||||
//去转义
|
||||
const unTransferred = () => {
|
||||
info.code = info.code.replace(/[\\]/g, ``)
|
||||
}
|
||||
|
||||
//清空输入框
|
||||
const clear = () => {
|
||||
info.code = ''
|
||||
}
|
||||
|
||||
const copyRes = async () => {
|
||||
copy(info.code)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white ">
|
||||
|
||||
<div>
|
||||
<!--
|
||||
tabSize: tab键前进的个数
|
||||
style: 自定义样式
|
||||
autofocus: 挂载后立即聚焦在编辑器
|
||||
indent-with-tab:绑定键盘tab事件
|
||||
extensions: 扩展,传数组
|
||||
|
||||
@ready="console.log('ready', $event)"
|
||||
@change="console.log('change', $event)"
|
||||
@focus="console.log('focus', $event)"
|
||||
@blur="console.log('blur', $event)"
|
||||
-->
|
||||
<codemirror
|
||||
v-model="info.code"
|
||||
placeholder="这里是代码..."
|
||||
:style="{ height: '400px' }"
|
||||
:autofocus="true"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="2"
|
||||
:extensions="info.extensions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-button type="primary" @click="formatJson">校验/格式化</el-button>
|
||||
<el-button type="primary" @click="compress">压缩</el-button>
|
||||
<el-button type="primary" @click="tran">转义</el-button>
|
||||
<el-button type="primary" @click="unTransferred">去转义</el-button>
|
||||
<!-- <el-button type="primary">unicode转中文</el-button> -->
|
||||
<!-- <el-button type="primary">中文转unicode</el-button> -->
|
||||
<el-button type="primary" @click="copyRes">复制</el-button>
|
||||
<el-button type="primary" @click="clear">清空</el-button>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 min-h-md bg-red-100 p-3 mb-3" v-show="info.isParseErr">
|
||||
<el-text type="danger">{{ info.parseErr }}</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,91 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { Md5 } from 'ts-md5'
|
||||
import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "MD5在线加密",
|
||||
encryptStr: '',
|
||||
encryptUpper32: '',
|
||||
encryptLower32: '',
|
||||
encryptUpper16: '',
|
||||
encryptLower16: '',
|
||||
})
|
||||
|
||||
//加密
|
||||
const encrypt = () => {
|
||||
info.encryptLower32 = Md5.hashStr(info.encryptStr)
|
||||
info.encryptUpper32 = info.encryptLower32.toUpperCase()
|
||||
info.encryptUpper16 = info.encryptUpper32.substring(8, 24)
|
||||
info.encryptLower16 = info.encryptLower32.substring(8, 24)
|
||||
}
|
||||
|
||||
//清空输入框
|
||||
const clear = () => {
|
||||
info.encryptStr = ''
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async (resStr: string) => {
|
||||
copy(resStr)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white ">
|
||||
<div>
|
||||
<el-input v-model="info.encryptStr" :rows="5" type="textarea" placeholder="请输入加密内容"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-button type="primary" @click="encrypt()" >加密</el-button>
|
||||
<el-button type="primary" @click="clear()">清空输入框</el-button>
|
||||
</div>
|
||||
|
||||
<div class="w-[30rem]">
|
||||
<el-input :value="info.encryptUpper32" class="mt-4" readonly>
|
||||
<template #prepend>
|
||||
<el-text>32位大写</el-text>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button @click="copyRes(info.encryptUpper32)">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-input :value="info.encryptLower32" class="mt-2" readonly>
|
||||
<template #prepend>
|
||||
<el-text>32位小写</el-text>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button @click="copyRes(info.encryptLower32)">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-input :value="info.encryptUpper16" class="mt-2" readonly>
|
||||
<template #prepend>
|
||||
<el-text>16位大写</el-text>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button @click="copyRes(info.encryptUpper16)">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-input :value="info.encryptLower16" class="mt-2" readonly>
|
||||
<template #prepend>
|
||||
<el-text>16位小写</el-text>
|
||||
</template>
|
||||
<template #append>
|
||||
<el-button @click="copyRes(info.encryptLower16)">复制</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
|
||||
const info = reactive({
|
||||
title: "在线markdown编辑器",
|
||||
content: '',
|
||||
})
|
||||
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="">
|
||||
<v-md-editor v-model="info.content" height="500px"></v-md-editor>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy } from '@/utils/string'
|
||||
import { toMorse, toText } from '@/utils/morse'
|
||||
const info = reactive({
|
||||
title: "摩斯电码",
|
||||
content: '支持中文的摩斯电码编码(Encode)解码(Decode)',
|
||||
tranRes: '',
|
||||
})
|
||||
|
||||
const toEncode = () => {
|
||||
info.tranRes = toMorse(info.content)
|
||||
|
||||
}
|
||||
|
||||
const toDecode = () => {
|
||||
info. tranRes = toText(info.content)
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
info.content = ''
|
||||
info.tranRes = ''
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async (resStr: string) => {
|
||||
copy(resStr)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white ">
|
||||
<div>
|
||||
<el-input type="textarea" :rows="8" v-model="info.content"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-button type="primary" @click="toEncode">摩斯编码</el-button>
|
||||
<el-button type="primary" @click="toDecode">摩斯解码</el-button>
|
||||
<el-button type="primary" @click="copyRes(info.tranRes)">复制结果</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>
|
||||
@@ -0,0 +1,50 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy, numberToChinese } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "数字转金额大写",
|
||||
content: '',
|
||||
tranRes: '',
|
||||
})
|
||||
|
||||
const tran = () => {
|
||||
//数字转中文
|
||||
info.tranRes = numberToChinese(parseInt(info.content))
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
info.content = ''
|
||||
info.tranRes = ''
|
||||
}
|
||||
|
||||
const copyRes = async (resStr: string) => {
|
||||
copy(resStr)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-input type="number" v-model="info.content" placeholder="请输入数字,最高13位数字"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-button type="primary" @click="tran">数字转中文大写</el-button>
|
||||
<el-button type="primary" @click="copyRes(info.tranRes)">复制结果</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>
|
||||
@@ -0,0 +1,81 @@
|
||||
<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: "色板",
|
||||
palettes: [
|
||||
{
|
||||
theme: 'last summer day', //
|
||||
item: ['#A7C5C5', '#DEE0D5', '#E2AC48', '#B96028', '#983C2D'],
|
||||
},
|
||||
{
|
||||
theme: 'Hazy Summer Afternoon', //
|
||||
item: ['#26C4A5', '#54D99F', '#ACE08C', '#E8E284', '#F2B652'],
|
||||
},
|
||||
{
|
||||
theme: 'Summering', //
|
||||
item: ['#FFBE4C', '#B33127', '#93EDFF', '#648FAD', '#FFFFFF'],
|
||||
},
|
||||
{
|
||||
theme: 'Bare August — Footcare Essentials', //
|
||||
item: ['#F2636F', '#F2858E', '#F2AEB4', '#F27999', '#F2D9D0'],
|
||||
},
|
||||
{
|
||||
theme: 'Big Vilnius Summer Loto', //
|
||||
item: ['#D90416', '#BF2A45', '#528C49', '#BF7B54', '#F28D8D'],
|
||||
},
|
||||
{
|
||||
theme: 'Illustration Design', //
|
||||
item: ['#D9D2D6', '#010326', '#4C5D73', '#808C65', '#F2E750'],
|
||||
},
|
||||
{
|
||||
theme: 'Beautiful tropical beach with blue sky and white clouds abstract texture background', //
|
||||
item: ['#04B2D9', '#CEECF2', '#04C4D9', '#15BFBF', '#D9D0C7'],
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
//copy
|
||||
const copyRes = async (resStr: string) => {
|
||||
copy(resStr)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="">
|
||||
<div class="flex flex-col">
|
||||
<div v-for="(item, index) in info.palettes" :key="index" class="flex flex-col mb-3">
|
||||
<div class="flex h-28 rounded-lg overflow-hidden items-center">
|
||||
<span
|
||||
v-for="(color, index) in item.item"
|
||||
:key="index"
|
||||
class="w-1/5 h-full color-palette"
|
||||
:style="'background-color:' + color + ';'"
|
||||
@click="copyRes(color)"
|
||||
>
|
||||
<div class="color-text hidden flex items-center justify-center h-full w-full" :style="'color:' + color + ';'" >{{ color }}</div>
|
||||
</span>
|
||||
<!-- @change="(val: any) => (changeCheckBox(val, 3)) -->
|
||||
</div>
|
||||
<!-- <div class="">
|
||||
<el-text>主题:{{ item.theme }}</el-text>
|
||||
</div> -->
|
||||
<el-divider v-if="index != item.item.length + 1"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.color-palette:hover .color-text{
|
||||
display: flex;
|
||||
filter: grayscale(1) contrast(999) invert(1)
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,181 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive,ref } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import QRCodeVue3 from 'qrcode-vue3'
|
||||
import { Delete, Plus } from '@element-plus/icons-vue'
|
||||
import { ElMessage, type UploadFile } from 'element-plus'
|
||||
const info = reactive({
|
||||
title: "二维码生成",
|
||||
content: '可在此输入文字或网址,右键图片另存为可保存图片',
|
||||
width: 200,
|
||||
height: 200,
|
||||
size: '200',
|
||||
sizeSelect: '',
|
||||
margin: 1,
|
||||
fileList: <string[]>[],
|
||||
fileUrl: '',
|
||||
preColor: '#000',
|
||||
bgColor: '#fff',
|
||||
qrKey: 1,
|
||||
errorCorrectionLevel: 'Q',
|
||||
})
|
||||
|
||||
const uploadLogo = ref()
|
||||
|
||||
//上传达到上限触发
|
||||
const handleExceed = () => {
|
||||
ElMessage({
|
||||
message: '上传数量已达上限,请清除后重新上传',
|
||||
type: 'warning',
|
||||
})
|
||||
}
|
||||
|
||||
//设置尺寸
|
||||
const setQRSize = () => {
|
||||
info.width = parseInt(info.size)
|
||||
info.height = parseInt(info.size)
|
||||
}
|
||||
|
||||
const handleChange = (file: UploadFile) => {
|
||||
info.fileList.push(file.url as string)
|
||||
info.fileUrl = info.fileList[0] ?? ''
|
||||
}
|
||||
|
||||
const handleRemove = (file: UploadFile) => {
|
||||
info.fileList = uploadLogo.value.handleRemove(file)
|
||||
}
|
||||
|
||||
|
||||
//生成二维码
|
||||
const gen = () => {
|
||||
//设置尺寸
|
||||
setQRSize()
|
||||
info.qrKey += 1
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 ml-4 flex-1 mr-3">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="flex justify-between w-full p-4 rounded-2xl bg-white">
|
||||
<div class="w-4/6 ">
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16 mr-2"><el-text>内容</el-text></div>
|
||||
<el-input v-model="info.content" type="textarea" rows="3" class="w-full" placeholder="可在此输入文字或网址"></el-input>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16 mr-2"><el-text>尺寸</el-text></div>
|
||||
<el-input v-model="info.size" class="">
|
||||
<template #append>
|
||||
<el-select v-model="info.size" class="w-32">
|
||||
<el-option label="常规 200px" value="200" />
|
||||
<el-option label="常规 300px" value="300" />
|
||||
<el-option label="适中 350px" value="350" />
|
||||
<el-option label="较大 500px" value="500" />
|
||||
<!-- 分割线 start -->
|
||||
<div class="flex justify-center bg-gray-200 w-full">
|
||||
<div class="h-[1px] bg-gray-200 w-4/5"></div>
|
||||
</div>
|
||||
<!-- 分割线 end -->
|
||||
<el-option label="超大 1000px" value="1000" />
|
||||
<el-option label="超大 1200px" value="1200" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<el-text class="w-16">纠错级别</el-text>
|
||||
<el-select v-model="info.errorCorrectionLevel" class="w-36">
|
||||
<el-option label="L 可遮挡 7%" value="L" />
|
||||
<el-option label="M 可遮挡 15%" value="M" />
|
||||
<el-option label="Q 可遮挡 25%" value="Q" />
|
||||
<el-option label="H 可遮挡 30%" value="H" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16"><el-text>边距</el-text></div>
|
||||
<el-input-number v-model="info.margin" :step="1" :min="0"/>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16"><el-text>颜色</el-text></div>
|
||||
<!-- <div class="w-16 flex flex-col items-center bg-gray-100 p-1 rounded-md mr-2">
|
||||
<el-text>前景色</el-text>
|
||||
<el-color-picker v-model="info.preColor" />
|
||||
</div> -->
|
||||
<div class="w-16 flex flex-col items-center bg-gray-100 p-1 rounded-md">
|
||||
<el-text>背景色</el-text>
|
||||
<el-color-picker v-model="info.bgColor" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16"><el-text>logo</el-text></div>
|
||||
<el-upload
|
||||
ref="uploadLogo"
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
list-type="picture-card"
|
||||
accept=".png,.ico,.jpg,.jpeg"
|
||||
:on-change="handleChange"
|
||||
:on-exceed="handleExceed">
|
||||
<el-icon><Plus /></el-icon>
|
||||
<template #file="{ file }">
|
||||
<div class="border-2 w-full border-blue-400">
|
||||
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="" />
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="flex mb-3">
|
||||
<div class="w-16"></div>
|
||||
<el-button type="primary" @click="gen">生成二维码</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-[200px] h-[200px]">
|
||||
<QRCodeVue3
|
||||
:key="info.qrKey"
|
||||
:width="info.width"
|
||||
:height="info.height"
|
||||
:value="info.content"
|
||||
:margin="info.margin"
|
||||
:qrOptions="{ typeNumber: 0, mode: 'Byte', errorCorrectionLevel: info.errorCorrectionLevel }"
|
||||
:imageOptions="{ hideBackgroundDots: true, imageSize: 0.4, margin: 0 }"
|
||||
:image="info.fileList[0]"
|
||||
:dotsOptions="{
|
||||
type: 'none',
|
||||
color: '#26249a',
|
||||
gradient: {
|
||||
type: 'linear',
|
||||
rotation: 0,
|
||||
colorStops: [
|
||||
{ offset: 0, color: '#26249a' },
|
||||
{ offset: 1, color: '#26249a' },
|
||||
],
|
||||
},
|
||||
}"
|
||||
:backgroundOptions="{ color: info.bgColor }"
|
||||
:cornersSquareOptions="{ type: 'square', color: '#000000' }"
|
||||
:cornersDotOptions="{ type: undefined, color: '#000000' }"
|
||||
fileExt="png"
|
||||
myclass="my-qur"
|
||||
imgclass="img-qr"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.my-button{
|
||||
background-color:red;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,56 @@
|
||||
<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: "生成随机数",
|
||||
startNum: 1,
|
||||
endNum: 100,
|
||||
resNum: 0,
|
||||
genStatus: false,
|
||||
})
|
||||
|
||||
const random = () => {
|
||||
info.genStatus = true
|
||||
let count = 0
|
||||
const genInterval = setInterval(() => {
|
||||
info.resNum = Math.floor(Math.random() * (info.endNum - info.startNum + 1)) + info.startNum;
|
||||
count++
|
||||
if (count > 10) {
|
||||
clearTimeout(genInterval)
|
||||
info.genStatus = false
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="flex flex-col items-center p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-input-number v-model="info.startNum" :min="0" size="large"/>
|
||||
<el-input-number v-model="info.endNum" :min="0" size="large" class="ml-5"/>
|
||||
</div>
|
||||
|
||||
<div class="h-60 text-8xl leading-[15rem]">
|
||||
{{ info.resNum }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-button @click="random" type="primary" size="large" v-if="!info.genStatus">生成</el-button>
|
||||
<el-button @click="random" type="primary" size="large" disabled v-else>生成...</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,156 @@
|
||||
<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
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//生成长度验证
|
||||
if (info.pwLen > 100) {
|
||||
ElMessage({
|
||||
message: "生成长度范围在1~100",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//生成数量验证
|
||||
if (info.pwNum > 100) {
|
||||
ElMessage({
|
||||
message: "生成数量范围在1~100",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
//清空旧数据
|
||||
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 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<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,165 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { Codemirror } from "vue-codemirror"
|
||||
import { lineNumbers } from '@codemirror/view';
|
||||
|
||||
const info = reactive({
|
||||
title: "正则表达式测试",
|
||||
extensions: [lineNumbers()],
|
||||
reg: '',
|
||||
content: `一些测试实例:
|
||||
邮箱1:[email protected]
|
||||
邮箱2:[email protected]
|
||||
手机号:13012341234
|
||||
url: http://tools.ranblogs.com、https://ranblogs.com
|
||||
IP: 192.168.0.1
|
||||
时间:2023-11-24
|
||||
`,
|
||||
matchRes: '',
|
||||
matchNum: 0,
|
||||
isAll: true,
|
||||
isCase: false,
|
||||
isParseErr: false,
|
||||
parseErr: '',
|
||||
regList: [
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配手机号',
|
||||
reg: '(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d)\\d{8}'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配网址URL',
|
||||
reg: '(http|ftp|https):\\\/\\\/[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-\\.,@?^=%&:/~\\+#]*[\\w\\-\\@?^=%&/~\\+#])?'
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配Email',
|
||||
reg: `\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}`
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配中文字符',
|
||||
reg: "[\u4e00-\u9fa5]"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配双字节字符(包含汉字)',
|
||||
reg: "[^\x00-\xff]+"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配18位身份证号',
|
||||
reg: "\\d{17}[0-9Xx]|\\d{15}"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配整数',
|
||||
reg: "[1-9]\\d*"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配IP(IPV4)',
|
||||
reg: "\\d{0,3}\\.\\d{0,3}\\.\\d{0,3}\\.\\d{0,3}"
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '匹配日期(年-月-日)',
|
||||
reg: "(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|[1][0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[3579][26])00))-02-29)"
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
//使用正则
|
||||
const useReg = (reg: string) => {
|
||||
info.reg = reg
|
||||
}
|
||||
|
||||
const clearOldRes = () => {
|
||||
info.matchNum = 0
|
||||
info.matchRes = ''
|
||||
}
|
||||
|
||||
//测试匹配(执行匹配)
|
||||
const execMatch = () => {
|
||||
//清除旧结果
|
||||
clearOldRes()
|
||||
//check
|
||||
if (info.reg == '') {
|
||||
return
|
||||
}
|
||||
|
||||
let rule = ''
|
||||
//是否全局
|
||||
rule = info.isAll === true ? 'g' : ''
|
||||
//是否区分大小写
|
||||
rule += info.isCase === true ? 'i' : ''
|
||||
|
||||
let pattern = new RegExp(info.reg, rule);
|
||||
let matchArr = info.content.match(pattern)
|
||||
|
||||
if (info.isAll === true) {
|
||||
if (matchArr?.length) {
|
||||
info.matchNum = matchArr?.length
|
||||
for (let index in matchArr) {
|
||||
info.matchRes += matchArr[index] + `\n`
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (matchArr?.[0]) {
|
||||
info.matchRes = matchArr[0]
|
||||
info.matchNum = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white ">
|
||||
<div>
|
||||
<codemirror
|
||||
v-model="info.content"
|
||||
placeholder=""
|
||||
:style="{ height: '300px' }"
|
||||
:autofocus="true"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="2"
|
||||
:extensions="info.extensions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-text class="font-bold">常用正则:</el-text>
|
||||
<ul class="flex flex-wrap">
|
||||
<li v-for="(item, index) in info.regList" :key="index">
|
||||
<el-button link type="primary" @click="useReg(item.reg)" class="mr-2 mt-2">{{ item.title }}</el-button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="flex items-center mt-4">
|
||||
<el-input v-model="info.reg" class="mr-2" placeholder="正则表达式"></el-input>
|
||||
|
||||
<div class="mr-2">
|
||||
<el-checkbox v-model="info.isAll" label="全局搜索" size="large"/>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-checkbox v-model="info.isCase" label="忽略大小写" size="large" />
|
||||
</div>
|
||||
|
||||
<el-button type="primary" @click="execMatch">测试匹配</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3">
|
||||
<el-text>共找到 {{ info.matchNum }} 处匹配结果</el-text>
|
||||
<el-input type="textarea" :rows="4" readonly v-model="info.matchRes"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,255 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { copy } from '@/utils/string'
|
||||
import { isBinary, isOctal, isDecimal, isHexadecimal } from '@/utils/verify'
|
||||
import { InfoFilled } from '@element-plus/icons-vue'
|
||||
import { scale58ToBase, scale62ToBase, scale64ToBase, baseToScale58, baseToScale62, baseToScale64 } from '@/utils/math'
|
||||
|
||||
const info = reactive({
|
||||
title: "常用进制转换",
|
||||
content: '',
|
||||
chooseTranOptions: '10',
|
||||
tranOptions: [
|
||||
{
|
||||
value: '2',
|
||||
label: '2进制',
|
||||
tranValue: '',
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
value: '8',
|
||||
label: '8进制',
|
||||
tranValue: '',
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
value: '10',
|
||||
label: '10进制',
|
||||
tranValue: '',
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
value: '16',
|
||||
label: '16进制',
|
||||
tranValue: '',
|
||||
desc: '',
|
||||
},
|
||||
{
|
||||
value: '32',
|
||||
label: '32进制',
|
||||
tranValue: '',
|
||||
desc: '数字 + 大写字母,不包含 ILOU 字符',
|
||||
},
|
||||
{
|
||||
value: '58',
|
||||
label: '58进制',
|
||||
tranValue: '',
|
||||
desc: '数字 + 大小写字母,不包含 0OlI 字符',
|
||||
},
|
||||
{
|
||||
value: '62',
|
||||
label: '62进制',
|
||||
tranValue: '',
|
||||
desc: '数字 + 大小写字母',
|
||||
},
|
||||
{
|
||||
value: '64',
|
||||
label: '64进制',
|
||||
tranValue: '',
|
||||
desc: '数字 + 大小写字母以及两个特殊字符 +/',
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
//高进制转换10进制 - int
|
||||
const tailScaleToBase = (content: string, scale: number): number => {
|
||||
let res = 0
|
||||
switch(scale) {
|
||||
case 58:
|
||||
res = scale58ToBase(content)
|
||||
break;
|
||||
case 62:
|
||||
res = scale62ToBase(content)
|
||||
break;
|
||||
case 64:
|
||||
res = scale64ToBase(content)
|
||||
break;
|
||||
default:
|
||||
ElMessage({
|
||||
message: "参数错误",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
//10进制转换高进制
|
||||
const baseToTailScale = (num: number, scale: number): string => {
|
||||
let res = ''
|
||||
switch(scale) {
|
||||
case 58:
|
||||
res = baseToScale58(num)
|
||||
break;
|
||||
case 62:
|
||||
res = baseToScale62(num)
|
||||
break;
|
||||
case 64:
|
||||
res = baseToScale64(num)
|
||||
break;
|
||||
default:
|
||||
ElMessage({
|
||||
message: "参数错误",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
//转换前 - check
|
||||
const tranCheck = () => {
|
||||
if (!info.content) {
|
||||
ElMessage({
|
||||
message: "请输入转换数值",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (info.chooseTranOptions == '2' && isBinary(info.content) == false) {
|
||||
ElMessage({
|
||||
message: "不是二进制值",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (info.chooseTranOptions == '8' && isOctal(info.content) == false) {
|
||||
ElMessage({
|
||||
message: "不是八进制值",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (info.chooseTranOptions == '10' && isDecimal(info.content) == false) {
|
||||
ElMessage({
|
||||
message: "不是十进制值",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
if (info.chooseTranOptions == '16' && isHexadecimal(info.content) == false) {
|
||||
ElMessage({
|
||||
message: "不是十六进制值",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
//转换
|
||||
const tran = () => {
|
||||
//转换前 - check
|
||||
if (!tranCheck()) {
|
||||
return
|
||||
}
|
||||
|
||||
//当前输入的内容转换成10进制
|
||||
let tranContent
|
||||
let chooseTranInt = parseInt(info.chooseTranOptions, 10)
|
||||
if (chooseTranInt <= 36) {
|
||||
tranContent = parseInt(info.content, chooseTranInt)
|
||||
} else {
|
||||
tranContent = tailScaleToBase(info.content, chooseTranInt)
|
||||
}
|
||||
|
||||
//用上面转换的10进制内容转换对应进制
|
||||
for (let index in info.tranOptions) {
|
||||
let valueInt = parseInt(info.tranOptions[index].value, 10)
|
||||
if (valueInt <= 36) {
|
||||
info.tranOptions[index].tranValue = tranContent.toString(valueInt)
|
||||
} else {
|
||||
info.tranOptions[index].tranValue = baseToTailScale(tranContent, valueInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async (index: any) => {
|
||||
copy(info.tranOptions[index].tranValue)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1 ">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div class="flex">
|
||||
<div class="mr-2 w-full">
|
||||
<el-input v-model="info.content">
|
||||
<template #prepend>
|
||||
<el-select v-model="info.chooseTranOptions" placeholder="Select" class="w-24">
|
||||
<el-option
|
||||
v-for="item in info.tranOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<el-button type="primary" @click="tran()">转换</el-button>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex flex-col">
|
||||
<el-table :data="info.tranOptions" border style="width: 100%">
|
||||
<el-table-column prop="label" label="进制" width="120">
|
||||
<template #default="scope">
|
||||
<div class="flex items-center">
|
||||
<span class="mr-1">{{ scope.row.label }}</span>
|
||||
<el-tooltip
|
||||
v-if="scope.row.desc != ''"
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
:content="scope.row.desc"
|
||||
placement="top-start"
|
||||
><el-icon ><InfoFilled /></el-icon></el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="tranValue" label="结果"/>
|
||||
<el-table-column prop="" label="操作" width="60">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.prevent="copyRes(scope.$index)"
|
||||
>
|
||||
复制
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,88 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from "vue"
|
||||
import SignImageCore from './SignImageCore.vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { UploadProps, UploadInstance, UploadRawFile, genFileId } from 'element-plus'
|
||||
import { autoDown } from '@/utils/file'
|
||||
import { Jh_getTimeStamp } from '@/utils/time'
|
||||
|
||||
// 图片处理
|
||||
const info = reactive({
|
||||
title:"在线编辑图片",
|
||||
//图片地址
|
||||
previewsImgUrl:"https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg",
|
||||
//获取处理完的图片
|
||||
getNewImg:(url: string) => {
|
||||
//下载
|
||||
autoDown(url, Jh_getTimeStamp() + '.png')
|
||||
},
|
||||
//用于刷新组件视图
|
||||
cKey: 1,
|
||||
})
|
||||
|
||||
//使用 upload ref
|
||||
const upload = ref<UploadInstance>()
|
||||
//使用 SignImageCore ref
|
||||
const refSignImageCore = ref<any>()
|
||||
|
||||
//上传文件发生变化
|
||||
const uploadChange: UploadProps['onChange'] = (file) => {
|
||||
//UploadFile转换url
|
||||
info.previewsImgUrl = URL.createObjectURL(file.raw!)
|
||||
// 更新组件
|
||||
info.cKey++
|
||||
}
|
||||
|
||||
//钩子 - 文件上传超出限制
|
||||
const uploadExceed: UploadProps['onExceed'] = (files) => {
|
||||
upload.value!.clearFiles()
|
||||
const file = files[0] as UploadRawFile
|
||||
file.uid = genFileId()
|
||||
upload.value!.handleStart(file)
|
||||
}
|
||||
|
||||
//保存图片
|
||||
const saveImg = () => {
|
||||
refSignImageCore.value!.save()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="flex flex-col mb-3 h-[38rem] p-4 rounded-2xl bg-white">
|
||||
<div class="mb-3 flex flex-row-reverse">
|
||||
<!-- save -->
|
||||
<el-button type="primary" class="ml-2" @click="saveImg">保存</el-button>
|
||||
<!-- upload -->
|
||||
<el-upload
|
||||
ref="upload"
|
||||
:limit="1"
|
||||
@exceed="uploadExceed"
|
||||
accept="image/*"
|
||||
@change="uploadChange"
|
||||
:auto-upload="false"
|
||||
:show-file-list="false"
|
||||
>
|
||||
<template #trigger>
|
||||
<el-button type="primary">选择图片</el-button>
|
||||
</template>
|
||||
</el-upload>
|
||||
</div>
|
||||
<!-- 图片处理框 -->
|
||||
<SignImageCore
|
||||
ref="refSignImageCore"
|
||||
:key="info.cKey"
|
||||
:dialogVisible.sync="true"
|
||||
:title="info.title"
|
||||
:imgUrl="info.previewsImgUrl"
|
||||
@getNewImg="info.getNewImg"
|
||||
></SignImageCore>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import 'tui-image-editor/dist/tui-image-editor.css'
|
||||
// import 'tui-color-picker/dist/tui-color-picker.css'
|
||||
import { base64ToBlod } from '@/utils/file'
|
||||
import ImageEditor from 'tui-image-editor'
|
||||
|
||||
// 中文菜单
|
||||
const locale_zh = {
|
||||
ZoomIn: '放大',
|
||||
ZoomOut: '缩小',
|
||||
Hand: '手掌',
|
||||
History: '历史',
|
||||
Resize: '调整宽高',
|
||||
Crop: '裁剪',
|
||||
DeleteAll: '全部删除',
|
||||
Delete: '删除',
|
||||
Undo: '撤销',
|
||||
Redo: '反撤销',
|
||||
Reset: '重置',
|
||||
Flip: '镜像',
|
||||
Rotate: '旋转',
|
||||
Draw: '画',
|
||||
Shape: '形状标注',
|
||||
Icon: '图标标注',
|
||||
Text: '文字标注',
|
||||
Mask: '遮罩',
|
||||
Filter: '滤镜',
|
||||
Bold: '加粗',
|
||||
Italic: '斜体',
|
||||
Underline: '下划线',
|
||||
Left: '左对齐',
|
||||
Center: '居中',
|
||||
Right: '右对齐',
|
||||
Color: '颜色',
|
||||
'Text size': '字体大小',
|
||||
Custom: '自定义',
|
||||
Square: '正方形',
|
||||
Apply: '应用',
|
||||
Cancel: '取消',
|
||||
'Flip X': 'X 轴',
|
||||
'Flip Y': 'Y 轴',
|
||||
Range: '区间',
|
||||
Stroke: '描边',
|
||||
Fill: '填充',
|
||||
Circle: '圆',
|
||||
Triangle: '三角',
|
||||
Rectangle: '矩形',
|
||||
Free: '曲线',
|
||||
Straight: '直线',
|
||||
Arrow: '箭头',
|
||||
'Arrow-2': '箭头2',
|
||||
'Arrow-3': '箭头3',
|
||||
'Star-1': '星星1',
|
||||
'Star-2': '星星2',
|
||||
Polygon: '多边形',
|
||||
Location: '定位',
|
||||
Heart: '心形',
|
||||
Bubble: '气泡',
|
||||
'Custom icon': '自定义图标',
|
||||
'Load Mask Image': '加载蒙层图片',
|
||||
Grayscale: '灰度',
|
||||
Blur: '模糊',
|
||||
Sharpen: '锐化',
|
||||
Emboss: '浮雕',
|
||||
'Remove White': '除去白色',
|
||||
Distance: '距离',
|
||||
Brightness: '亮度',
|
||||
Noise: '噪音',
|
||||
'Color Filter': '彩色滤镜',
|
||||
Sepia: '棕色',
|
||||
Sepia2: '棕色2',
|
||||
Invert: '负片',
|
||||
Pixelate: '像素化',
|
||||
Threshold: '阈值',
|
||||
Tint: '色调',
|
||||
Multiply: '正片叠底',
|
||||
Blend: '混合色',
|
||||
Width: '宽度',
|
||||
Height: '高度',
|
||||
'Lock Aspect Ratio': '锁定宽高比例'
|
||||
}
|
||||
|
||||
// 画布组件自定义样式
|
||||
const customTheme = {
|
||||
"common.bi.image": "", // 左上角logo图片
|
||||
"common.bisize.width": "0px",
|
||||
"common.bisize.height": "0px",
|
||||
"common.backgroundImage": "none",
|
||||
"common.backgroundColor": "#f3f4f6",
|
||||
"common.border": "1px solid #333",
|
||||
|
||||
// header
|
||||
"header.backgroundImage": "none",
|
||||
"header.backgroundColor": "#f3f4f6",
|
||||
"header.border": "0px",
|
||||
|
||||
// load button
|
||||
"loadButton.backgroundColor": "#fff",
|
||||
"loadButton.border": "1px solid #ddd",
|
||||
"loadButton.color": "#222",
|
||||
"loadButton.fontFamily": "NotoSans, sans-serif",
|
||||
"loadButton.fontSize": "12px",
|
||||
"loadButton.display": "none", // 可以直接隐藏掉
|
||||
|
||||
// download button
|
||||
"downloadButton.backgroundColor": "#fdba3b",
|
||||
"downloadButton.border": "1px solid #fdba3b",
|
||||
"downloadButton.color": "#fff",
|
||||
"downloadButton.fontFamily": "NotoSans, sans-serif",
|
||||
"downloadButton.fontSize": "12px",
|
||||
"downloadButton.display": "none", // 可以直接隐藏掉
|
||||
|
||||
// icons default
|
||||
"menu.normalIcon.color": "#8a8a8a",
|
||||
"menu.activeIcon.color": "#555555",
|
||||
"menu.disabledIcon.color": "#ccc",
|
||||
"menu.hoverIcon.color": "#e9e9e9",
|
||||
"submenu.normalIcon.color": "#8a8a8a",
|
||||
"submenu.activeIcon.color": "#e9e9e9",
|
||||
|
||||
"menu.iconSize.width": "24px",
|
||||
"menu.iconSize.height": "24px",
|
||||
"submenu.iconSize.width": "32px",
|
||||
"submenu.iconSize.height": "32px",
|
||||
|
||||
// submenu primary color
|
||||
"submenu.backgroundColor": "#1e1e1e",
|
||||
"submenu.partition.color": "#858585",
|
||||
|
||||
// submenu labels
|
||||
"submenu.normalLabel.color": "#858585",
|
||||
"submenu.normalLabel.fontWeight": "lighter",
|
||||
"submenu.activeLabel.color": "#fff",
|
||||
"submenu.activeLabel.fontWeight": "lighter",
|
||||
|
||||
// checkbox style
|
||||
"checkbox.border": "1px solid #ccc",
|
||||
"checkbox.backgroundColor": "#fff",
|
||||
|
||||
// rango style
|
||||
"range.pointer.color": "#fff",
|
||||
"range.bar.color": "#666",
|
||||
"range.subbar.color": "#d1d1d1",
|
||||
|
||||
"range.disabledPointer.color": "#414141",
|
||||
"range.disabledBar.color": "#282828",
|
||||
"range.disabledSubbar.color": "#414141",
|
||||
|
||||
"range.value.color": "#fff",
|
||||
"range.value.fontWeight": "lighter",
|
||||
"range.value.fontSize": "11px",
|
||||
"range.value.border": "1px solid #353535",
|
||||
"range.value.backgroundColor": "#151515",
|
||||
"range.title.color": "#fff",
|
||||
"range.title.fontWeight": "lighter",
|
||||
|
||||
// colorpicker style
|
||||
"colorpicker.button.border": "1px solid #1e1e1e",
|
||||
"colorpicker.title.color": "#fff",
|
||||
};
|
||||
|
||||
// props
|
||||
const props = defineProps({
|
||||
dialogVisible: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
imgUrl: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
|
||||
});
|
||||
const emit = defineEmits()
|
||||
const instance = ref()
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
init() // 页面创建完成后调用
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const init = ()=> {
|
||||
instance.value = new ImageEditor(document.querySelector('#tui-image-editor'), {
|
||||
includeUI: {
|
||||
loadImage: {
|
||||
path: props.imgUrl,
|
||||
name: 'image'
|
||||
},
|
||||
menu: ['resize', 'crop', 'rotate', 'draw', 'shape', 'icon', 'text', 'filter'], // 底部菜单按钮列表 隐藏镜像flip和遮罩mask
|
||||
initMenu: 'none', // 默认打开的菜单项
|
||||
menuBarPosition: 'left', // 菜单所在的位置
|
||||
locale: locale_zh, // 本地化语言为中文
|
||||
theme: customTheme // 自定义样式
|
||||
},
|
||||
cssMaxWidth: 400, // canvas 最大宽度
|
||||
cssMaxHeight: 500 // canvas 最大高度
|
||||
})
|
||||
document.getElementsByClassName('tui-image-editor-main')[0].style.top = '0' // 调整图片显示位置
|
||||
document.getElementsByClassName(
|
||||
'tie-btn-reset tui-image-editor-item help'
|
||||
)[0].style.display = 'none' // 隐藏顶部重置按钮
|
||||
}
|
||||
|
||||
// 保存图片,并上传
|
||||
const save = () => {
|
||||
// base64 文件
|
||||
const base64String = instance.value.toDataURL()
|
||||
//转换blod
|
||||
const url = base64ToBlod(base64String)
|
||||
//传递给父组件
|
||||
emit('getNewImg', url);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
save
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="tui-image-editor"></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, computed } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy } from '@/utils/string'
|
||||
|
||||
const info = reactive({
|
||||
title: "文本去重",
|
||||
content: '',
|
||||
tranRes: '',
|
||||
})
|
||||
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
info.content = ''
|
||||
}
|
||||
|
||||
|
||||
const removeDuplicate = computed(() => {
|
||||
let tmpArr = info.content.split('\n')
|
||||
let resultSet = new Set(tmpArr)
|
||||
let fromResArr = Array.from(resultSet)
|
||||
info.tranRes = fromResArr.join("\n")
|
||||
return info.tranRes
|
||||
})
|
||||
|
||||
//copy
|
||||
const copyRes = async () => {
|
||||
copy(info.tranRes)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-input type="textarea" :rows="8" v-model="info.content"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<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="removeDuplicate"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,142 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, shallowRef, onBeforeUnmount } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import html2canvas from "html2canvas";
|
||||
import '@wangeditor/editor/dist/css/style.css' // 引入富文本 css
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' //富文本组件
|
||||
// import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "文本转图片",
|
||||
mode: 'default',
|
||||
convasWidth: 860,
|
||||
convasBackgroundColor: '#fff',
|
||||
downExt: '.png',
|
||||
downExts: [
|
||||
{
|
||||
value: '.png',
|
||||
label: '.png'
|
||||
},
|
||||
{
|
||||
value: '.jpg',
|
||||
label: '.jpg'
|
||||
},
|
||||
]
|
||||
})
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef()
|
||||
|
||||
// 内容 HTML
|
||||
const valueHtml = ref(`文字转图片演示😀
|
||||
拥有丰富的样式选择
|
||||
可自由调整宽度背景色
|
||||
支持一键导出为长图
|
||||
`)
|
||||
|
||||
// 绑定 需要把那个内容生成图片
|
||||
const poster = ref();
|
||||
|
||||
// 工具栏配置
|
||||
const toolbarConfig = {
|
||||
excludeKeys: [
|
||||
"uploadImage",
|
||||
"group-video",
|
||||
"insertLink",
|
||||
]
|
||||
}
|
||||
|
||||
//编辑器配置
|
||||
const editorConfig = { placeholder: '请输入内容...' }
|
||||
|
||||
const handleCreated = (editor) => {
|
||||
editorRef.value = editor // 记录 editor 实例,重要!
|
||||
}
|
||||
|
||||
const goDown =() => {
|
||||
html2canvas(poster.value, {
|
||||
backgroundColor: info.convasBackgroundColor,//海报的背景颜色
|
||||
useCORS: true, // 允许跨域
|
||||
width: info.convasWidth, //生成海报的w
|
||||
}).then(canvas=>{
|
||||
// canvas 其实就是我们所讲的res 的意思 返回报文的意思
|
||||
let baseImg = canvas.toDataURL("image/png");
|
||||
if (info.downExt == '.jpg') {
|
||||
baseImg = canvas.toDataURL("image/jpg");
|
||||
}
|
||||
//创建a标签
|
||||
let save = document.createElement('a');
|
||||
// <a href=''></a>
|
||||
save.href = baseImg;
|
||||
// 下载的名字
|
||||
save.download = 'yz'
|
||||
// 直接回调a的点击事件
|
||||
save.click()
|
||||
})
|
||||
}
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<div class="">
|
||||
|
||||
<div class="flex items-center">
|
||||
<el-text class="w-20">宽度:</el-text>
|
||||
<el-slider v-model="info.convasWidth" show-input size="large" :min="260" :max="1920"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 flex items-center justify-between">
|
||||
<div class="">
|
||||
<el-text>背景颜色:</el-text>
|
||||
<el-color-picker v-model="info.convasBackgroundColor" size="large" />
|
||||
</div>
|
||||
<div>
|
||||
<el-select v-model="info.downExt" class="" placeholder="Select">
|
||||
<el-option
|
||||
v-for="item in info.downExts"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button type="primary" class="ml-2" @click="goDown">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border relative z-10 mt-3">
|
||||
<Toolbar
|
||||
class="border-b"
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
:mode="info.mode"
|
||||
/>
|
||||
<Editor
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
:mode="info.mode"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
<div ref="poster" class="absolute top-0 -z-10" v-html="valueHtml"></div>
|
||||
</div>
|
||||
|
||||
<!-- v-html="valueHtml" -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
@@ -0,0 +1,141 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, reactive } from 'vue'
|
||||
import { VideoPause,VideoPlay,CopyDocument } from '@element-plus/icons-vue'
|
||||
import { Jh_getTimeStamp,Jh_timeStampToTime,Jh_convertTimeStamp } from '@/utils/time'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy } from '@/utils/string'
|
||||
const info = reactive({
|
||||
title: "时间戳转换",
|
||||
nowTime: Jh_getTimeStamp(),
|
||||
isPlay: true,
|
||||
waitTimeStamp: Jh_getTimeStamp(),//待转换的时间戳
|
||||
tranTimeStamp: 0, //转换后的时间戳
|
||||
waitDate: Jh_timeStampToTime(Jh_getTimeStamp(), '{y}-{m}-{d} {h}:{i}:{s}'), //待转换时间
|
||||
tranDate: '', //转换后时间
|
||||
chooseTranStampOption: '0',
|
||||
chooseTranDateOption: '0',
|
||||
tranOptions: [
|
||||
{
|
||||
value: '0',
|
||||
label: '毫秒(ms)'
|
||||
},
|
||||
{
|
||||
value: '1',
|
||||
label: '秒(s)'
|
||||
}
|
||||
]
|
||||
})
|
||||
let timer: any
|
||||
|
||||
//开始定时任务
|
||||
const start = () => {
|
||||
if (!timer) {
|
||||
timer = setInterval(() => {
|
||||
info.nowTime = Jh_getTimeStamp()
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
//停止定时任务
|
||||
const stop = () => {
|
||||
clearInterval(timer)
|
||||
timer = null
|
||||
}
|
||||
|
||||
//进入
|
||||
onMounted(() => {
|
||||
start()
|
||||
})
|
||||
|
||||
//消亡
|
||||
onUnmounted(() => {
|
||||
stop()
|
||||
})
|
||||
|
||||
|
||||
const isPlayChange = () => {
|
||||
if (info.isPlay == true) {
|
||||
info.isPlay = false
|
||||
stop()
|
||||
} else {
|
||||
info.isPlay = true
|
||||
start()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//时间日期相互转换
|
||||
const timeTran = (type: string) => {
|
||||
if (type == 'toStamp') {
|
||||
info.tranTimeStamp = info.chooseTranStampOption == '0' ? Jh_convertTimeStamp(info.waitDate) : Jh_convertTimeStamp(info.waitDate) / 1000
|
||||
} else {
|
||||
//转换number类型
|
||||
if (typeof(info.waitTimeStamp) === 'string') info.waitTimeStamp = parseInt(info.waitTimeStamp as string)
|
||||
info.tranDate = Jh_timeStampToTime(info.waitTimeStamp, '{y}-{m}-{d} {h}:{i}:{s}')
|
||||
}
|
||||
}
|
||||
|
||||
//复制时间戳
|
||||
const copyRes = async () => {
|
||||
copy('' + info.nowTime)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 ml-4 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
<div class="flex flex-col p-4 rounded-2xl bg-white">
|
||||
<div class="flex flex-direction">
|
||||
<el-text class="mr-2 w-12">现在</el-text>
|
||||
<el-button class="mr-3" link @click="copyRes()">{{ info.nowTime }} <el-icon class="ml-1 mr-1"><CopyDocument /></el-icon></el-button>
|
||||
<el-button v-if="info.isPlay" type="danger" link class="flex items-center" @click="isPlayChange()"><el-icon class="mr-1" size="16"><VideoPlay/></el-icon>停止</el-button>
|
||||
<el-button v-else="info.isPlay" type="primary" link class="flex items-center" @click="isPlayChange()"><el-icon class="mr-1" size="16"><VideoPause /></el-icon>开始</el-button>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-direction mt-4 justify-start">
|
||||
<el-text class="mr-2 w-12">时间戳</el-text>
|
||||
<el-input v-model="info.waitTimeStamp" class="h-8 mr-2 w-60 max-w-[30%]" placeholder="毫秒/秒">
|
||||
<!-- <template #append>
|
||||
<el-select v-model="info.chooseTranDateOption" placeholder="Select" class="w-24">
|
||||
<el-option
|
||||
v-for="item in info.tranOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template> -->
|
||||
</el-input>
|
||||
<el-button class="mr-2 max-w-[25%]" @click="timeTran('toDate')">转换</el-button>
|
||||
<el-input v-model="info.tranDate" class="h-8 w-72 mr-2 max-w-[30%]" placeholder="北京时间"></el-input>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-direction mt-4 justify-start">
|
||||
<el-text class="mr-2 w-12">时间</el-text>
|
||||
<el-input v-model="info.waitDate" class="h-8 mr-2 w-60 max-w-[30%]"></el-input>
|
||||
<el-button class="mr-2 max-w-[25%]" @click="timeTran('toStamp')">转换</el-button>
|
||||
<el-input v-model="info.tranTimeStamp" :value="info.tranTimeStamp == 0 ? '' : info.tranTimeStamp" class="h-8 w-72 mr-2 max-w-[30%]" placeholder="时间戳">
|
||||
<template #prepend>
|
||||
<el-select v-model="info.chooseTranStampOption" placeholder="Select" class="w-24">
|
||||
<el-option
|
||||
v-for="item in info.tranOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white mt-5">
|
||||
<div class="text-xl mt-5 mb-5">时间戳</div>
|
||||
<el-text>Unix 时间戳是从1970年1月1日(UTC/GMT的午夜)开始所经过的秒数,不考虑闰秒。</el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,70 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { copy } from '@/utils/string'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
const info = reactive({
|
||||
title: "UUID生成器",
|
||||
resStr: '',
|
||||
pwNum: 1,
|
||||
autosize: {
|
||||
minRows: 5
|
||||
},
|
||||
})
|
||||
|
||||
//生成
|
||||
const gen = () => {
|
||||
//生成数量验证
|
||||
if (info.pwNum > 100) {
|
||||
ElMessage({
|
||||
message: "生成数量范围在1~100",
|
||||
type: "warning",
|
||||
duration: 1500
|
||||
})
|
||||
return
|
||||
}
|
||||
//清空旧数据
|
||||
info.resStr = ''
|
||||
|
||||
//按数量生成密码
|
||||
for (let i = 1; i <= info.pwNum; i++) {
|
||||
let ext = '\n'
|
||||
if (i == info.pwNum) {
|
||||
ext = ''
|
||||
}
|
||||
info.resStr += uuidv4() + ext
|
||||
}
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async (resStr: string) => {
|
||||
copy(resStr)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-input v-model="info.pwNum" placeholder="范围1~100" class="" max="100" type="number">
|
||||
<template #prepend>生成数量:</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="mt-3 mb-3">
|
||||
<el-button type="primary" @click="gen">生成UUID</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,83 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import { Codemirror } from "vue-codemirror"
|
||||
import { lineNumbers } from '@codemirror/view';
|
||||
import { copy } from '@/utils/string'
|
||||
|
||||
|
||||
const info = reactive({
|
||||
title: "Unicode转中文",
|
||||
extensions: [lineNumbers()],
|
||||
content: '',
|
||||
tranRes: '',
|
||||
})
|
||||
|
||||
const clearRes = () => {
|
||||
info.tranRes = ''
|
||||
}
|
||||
|
||||
//to zh
|
||||
//值转换中文
|
||||
const toZH = () => {
|
||||
//clear
|
||||
clearRes()
|
||||
let str = info.content
|
||||
str = str.replace(/\\/g, "%");
|
||||
// 转换中文
|
||||
str = unescape(str);
|
||||
info.tranRes = str
|
||||
}
|
||||
//to unicode
|
||||
const toUnicode = () => {
|
||||
//clear
|
||||
clearRes()
|
||||
//只转换中文
|
||||
for (let i = 0; i < info.content.length; i++) {
|
||||
if (/^[\u4E00-\u9FA5\uF900-\uFA2D]+$/.test(info.content[i])) {
|
||||
let code = info.content.charCodeAt(i).toString(16)
|
||||
info.tranRes += '\\u' + code
|
||||
} else {
|
||||
info.tranRes += info.content[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//copy
|
||||
const copyRes = async () => {
|
||||
copy(info.tranRes)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<codemirror
|
||||
v-model="info.content"
|
||||
placeholder=""
|
||||
:style="{ height: '200px' }"
|
||||
:autofocus="true"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="2"
|
||||
:extensions="info.extensions"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<el-button type="primary" @click="toZH">unicode转中文</el-button>
|
||||
<el-button type="primary" @click="toUnicode">中文转unicode</el-button>
|
||||
<el-button type="primary" @click="copyRes">复制结果</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>
|
||||
@@ -0,0 +1,415 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const lengthInfo: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
km: '',
|
||||
m: '',
|
||||
dm: '',
|
||||
cm: '',
|
||||
mm: '',
|
||||
ha: '',
|
||||
tradition_mu: '',
|
||||
tradition_fen: '',
|
||||
tradition_mill: '',
|
||||
tradition_hao: '',
|
||||
eng_nmi: '',
|
||||
eng_mi: '',
|
||||
eng_acre: '',
|
||||
eng_rd: '',
|
||||
eng_yd: '',
|
||||
eng_ft: '',
|
||||
eng_in: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in lengthInfo) {
|
||||
lengthInfo[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成平方米
|
||||
switch (key) {
|
||||
case 'km':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * (1000 * 1000)
|
||||
break;
|
||||
case 'm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 1
|
||||
break;
|
||||
case 'dm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.01
|
||||
break;
|
||||
case 'cm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.0001
|
||||
break;
|
||||
case 'mm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.000001
|
||||
break;
|
||||
case 'ha':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 10000
|
||||
break;
|
||||
case 'tradition_mu':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 666.6666667
|
||||
lengthInfo.tradition_mu = parseFloat(lengthInfo[key] as string) * 1
|
||||
break;
|
||||
case 'tradition_fen':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 66.67
|
||||
lengthInfo.tradition_mu = parseFloat(lengthInfo[key] as string) * 0.1
|
||||
break;
|
||||
case 'tradition_mill':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 6.67
|
||||
lengthInfo.tradition_mu = parseFloat(lengthInfo[key] as string) * 0.01
|
||||
break;
|
||||
case 'tradition_hao':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.67
|
||||
lengthInfo.tradition_mu = parseFloat(lengthInfo[key] as string) * 0.001
|
||||
break;
|
||||
case 'eng_nmi':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * (1852 * 1852)
|
||||
break;
|
||||
case 'eng_mi':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 2589988.110336
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 27878400
|
||||
break;
|
||||
case 'eng_acre':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 4046.8564224
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 43560
|
||||
break;
|
||||
case 'eng_rd':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 25.2928526
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 272.25
|
||||
break;
|
||||
case 'eng_yd':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.8361274
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 9
|
||||
break;
|
||||
case 'eng_ft':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.092903
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 1
|
||||
break;
|
||||
case 'eng_in':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.0006452
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 0.0069444
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//平方米转换成其他单位
|
||||
let mVal = lengthInfo.m as number
|
||||
//传统面积处理
|
||||
let muVal = mVal * 0.0015
|
||||
if (['tradition_mu', 'tradition_fen', 'tradition_mill', 'tradition_hao'].includes(key)) {
|
||||
muVal = (lengthInfo.tradition_mu == '' ? muVal : lengthInfo.tradition_mu) as number
|
||||
}
|
||||
//英制
|
||||
let engFtVal = mVal / 0.092903
|
||||
if (['eng_mi', 'eng_acre', 'eng_rd', 'eng_yd', 'eng_ft', 'eng_in'].includes(key)) {
|
||||
engFtVal = (lengthInfo.eng_ft == '' ? engFtVal : lengthInfo.eng_ft) as number
|
||||
}
|
||||
|
||||
lengthInfo.km = mVal * 0.000001
|
||||
lengthInfo.dm = mVal * 100
|
||||
lengthInfo.cm = mVal * 10000
|
||||
lengthInfo.mm = mVal * 1000000
|
||||
lengthInfo.ha = mVal * 0.0001
|
||||
|
||||
lengthInfo.tradition_mu = muVal
|
||||
lengthInfo.tradition_fen = muVal / 0.1
|
||||
lengthInfo.tradition_mill = muVal / 0.01
|
||||
lengthInfo.tradition_hao = muVal / 0.001
|
||||
|
||||
lengthInfo.eng_nmi = mVal / (1852 * 1852)
|
||||
lengthInfo.eng_mi = engFtVal / 27878400
|
||||
lengthInfo.eng_acre = engFtVal / 43560
|
||||
lengthInfo.eng_rd = engFtVal / 272.25
|
||||
lengthInfo.eng_yd = engFtVal / 9
|
||||
lengthInfo.eng_ft = engFtVal
|
||||
lengthInfo.eng_in = engFtVal / 0.0069444
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">国际面积单位(公制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方千米(km<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.km"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('km')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方米(m<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.m"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('m')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方分米(dm<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.dm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('dm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方厘米(cm<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.cm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('cm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方毫米(mm<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.mm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">公顷(ha)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.ha"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('ha')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">中国传统面积单位(市制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">亩</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_mu"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_mu')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">分</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_fen"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_fen')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">厘</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_mill"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_mill')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_hao"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_hao')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">英制面积单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方海里(nmi<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_nmi"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_nmi')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方英里(mi<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_mi"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_mi')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英亩(acre)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_acre"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_acre')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方竿(rd<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_rd"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_rd')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方码(yd<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_yd"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_yd')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方英尺(ft<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_ft"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_ft')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">平方英寸(in<sup>2</sup>)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_in"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_in')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,172 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const info: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
wh: '',
|
||||
mmwh: '',
|
||||
kwh: '',
|
||||
mwh: '',
|
||||
j: '',
|
||||
kj: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in info) {
|
||||
info[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成瓦
|
||||
switch (key) {
|
||||
case 'wh':
|
||||
info.wh = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'mmwh':
|
||||
info.wh = parseFloat(info[key] as string) * 0.001
|
||||
break;
|
||||
case 'kwh':
|
||||
info.wh = parseFloat(info[key] as string) * 1000
|
||||
break;
|
||||
case 'mwh':
|
||||
info.wh = parseFloat(info[key] as string) * 1000000
|
||||
break;
|
||||
case 'j':
|
||||
info.wh = parseFloat(info[key] as string) * 0.0002777777777777778
|
||||
break;
|
||||
case 'kj':
|
||||
info.wh = parseFloat(info[key] as string) * 0.2777777777777778
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//转换成其他单位
|
||||
let val = info.wh as number
|
||||
info.mmwh = val / 0.001
|
||||
info.kwh = val / 1000
|
||||
info.mwh = val / 1000000
|
||||
info.j = val / 0.0002777777777777778
|
||||
info.kj = info.j / 1000
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">热量单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">瓦(Wh)</el-text>
|
||||
<el-input
|
||||
v-model="info.wh"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('wh')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫瓦时(mWh)</el-text>
|
||||
<el-input
|
||||
v-model="info.mmwh"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mmwh')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">千瓦时(kWh)</el-text>
|
||||
<el-input
|
||||
v-model="info.kwh"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('kwh')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">兆瓦时(MWh)</el-text>
|
||||
<el-input
|
||||
v-model="info.mwh"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mwh')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">焦(J)</el-text>
|
||||
<el-input
|
||||
v-model="info.j"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('j')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">千焦(kJ)</el-text>
|
||||
<el-input
|
||||
v-model="info.kj"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('kj')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,512 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const lengthInfo: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
km: '',
|
||||
m: '',
|
||||
dm: '',
|
||||
cm: '',
|
||||
mm: '',
|
||||
dmm: '',
|
||||
um: '',
|
||||
nm: '',
|
||||
tradition_li: '',
|
||||
tradition_zhang: '',
|
||||
tradition_chi: '',
|
||||
tradition_cun: '',
|
||||
tradition_fen: '',
|
||||
tradition_mill: '',
|
||||
tradition_hao: '',
|
||||
eng_nmi: '',
|
||||
eng_fm: '',
|
||||
eng_mi: '',
|
||||
eng_fur: '',
|
||||
eng_yd: '',
|
||||
eng_ft: '',
|
||||
eng_in: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in lengthInfo) {
|
||||
lengthInfo[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成米
|
||||
switch (key) {
|
||||
case 'km':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 1000
|
||||
break;
|
||||
case 'm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 1
|
||||
break;
|
||||
case 'dm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.1
|
||||
break;
|
||||
case 'cm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.01
|
||||
break;
|
||||
case 'mm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.001
|
||||
break;
|
||||
case 'dmm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.0001
|
||||
break;
|
||||
case 'um':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.000001
|
||||
break;
|
||||
case 'nm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.000000001
|
||||
break;
|
||||
case 'tradition_li':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 500
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 1500
|
||||
break;
|
||||
case 'tradition_zhang':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 3.3333333
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 10
|
||||
break;
|
||||
case 'tradition_chi':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.3
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 1
|
||||
break;
|
||||
case 'tradition_cun':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.03
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 0.1
|
||||
break;
|
||||
case 'tradition_fen':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.003
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 0.01
|
||||
break;
|
||||
case 'tradition_mill':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.0003
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 0.001
|
||||
break;
|
||||
case 'tradition_hao':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.00003
|
||||
lengthInfo.tradition_chi = parseFloat(lengthInfo[key] as string) * 0.0001
|
||||
break;
|
||||
case 'eng_nmi':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 1852
|
||||
break;
|
||||
case 'eng_fm':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 1.852
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 6
|
||||
break;
|
||||
case 'eng_mi':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 1609.34
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 5280
|
||||
break;
|
||||
case 'eng_fur':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 201.168
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 660
|
||||
break;
|
||||
case 'eng_yd':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.9144
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 3
|
||||
break;
|
||||
case 'eng_ft':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.3048
|
||||
lengthInfo.eng_ft = parseFloat(lengthInfo[key] as string) * 1
|
||||
break;
|
||||
case 'eng_in':
|
||||
lengthInfo.m = parseFloat(lengthInfo[key] as string) * 0.0254
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//米转换成其他单位
|
||||
let mVal = lengthInfo.m as number
|
||||
//中国传统单位处理
|
||||
let chiVal = mVal * 3
|
||||
if (['tradition_li', 'tradition_zhang', 'tradition_chi', 'tradition_cun', 'tradition_fen', 'tradition_mill', 'tradition_hao'].includes(key)) {
|
||||
chiVal = (lengthInfo.tradition_chi == '' ? chiVal : lengthInfo.tradition_chi) as number
|
||||
}
|
||||
//英制处理
|
||||
let engFtVal = mVal / 0.3048
|
||||
if (['eng_fm', 'eng_mi', 'eng_fur', 'eng_yd', 'eng_ft'].includes(key)) {
|
||||
engFtVal = (lengthInfo.eng_ft == '' ? mVal / 0.3048 : lengthInfo.eng_ft) as number
|
||||
}
|
||||
lengthInfo.km = mVal / 1000
|
||||
lengthInfo.dm = mVal * 10
|
||||
lengthInfo.cm = mVal * 100
|
||||
lengthInfo.mm = mVal * 1000
|
||||
lengthInfo.dmm = mVal * 10000
|
||||
lengthInfo.um = mVal * 1000000
|
||||
lengthInfo.nm = mVal * 1000000000
|
||||
lengthInfo.tradition_li = mVal / 500
|
||||
lengthInfo.tradition_chi = chiVal
|
||||
lengthInfo.tradition_zhang = chiVal / 10
|
||||
lengthInfo.tradition_cun = chiVal / 0.1
|
||||
lengthInfo.tradition_fen = chiVal / 0.01
|
||||
lengthInfo.tradition_mill = chiVal / 0.001
|
||||
lengthInfo.tradition_hao = chiVal / 0.0001
|
||||
lengthInfo.eng_nmi = mVal / 1852
|
||||
lengthInfo.eng_ft = engFtVal
|
||||
lengthInfo.eng_fm = engFtVal / 6
|
||||
lengthInfo.eng_mi = engFtVal / 5280
|
||||
lengthInfo.eng_fur = engFtVal / 660
|
||||
lengthInfo.eng_yd = engFtVal / 3
|
||||
lengthInfo.eng_in = engFtVal * 12
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">国际长度单位(公制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">公里(km)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.km"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('km')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">米(m)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.m"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('m')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">分米(dm)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.dm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('dm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">厘米(cm)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.cm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('cm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫米(mm)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.mm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">丝(dmm)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.dmm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('dmm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">微米(um)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.um"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('um')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">纳米(nm)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.nm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('nm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">中国传统长度单位(市制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">里</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_li"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_li')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">丈</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_zhang"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_zhang')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">尺</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_chi"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_chi')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">寸</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_cun"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_cun')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">分</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_fen"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_fen')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">厘</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_mill"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_mill')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.tradition_hao"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('tradition_hao')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider content-position="left">英制长度单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">海里(nmi)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_nmi"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_nmi')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英寻(fm)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_fm"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_fm')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英里(mi)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_mi"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_mi')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">弗隆(fur)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_fur"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_fur')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">码(yd)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_yd"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_yd')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英尺(ft)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_ft"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_ft')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英寸(in)</el-text>
|
||||
<el-input
|
||||
v-model="lengthInfo.eng_in"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
type="number"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_in')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 45%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 5rem/* 80px */;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,155 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const info: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
w: '',
|
||||
mmw: '',
|
||||
kw: '',
|
||||
mw: '',
|
||||
gw: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in info) {
|
||||
info[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成瓦
|
||||
switch (key) {
|
||||
case 'w':
|
||||
info.w = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'mmw':
|
||||
info.w = parseFloat(info[key] as string) * 0.001
|
||||
break;
|
||||
case 'kw':
|
||||
info.w = parseFloat(info[key] as string) * 1000
|
||||
break;
|
||||
case 'mw':
|
||||
info.w = parseFloat(info[key] as string) * 1000000
|
||||
break;
|
||||
case 'gw':
|
||||
info.w = parseFloat(info[key] as string) * 1000000000
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//转换成其他单位
|
||||
let val = info.w as number
|
||||
info.mmw = val / 0.001
|
||||
info.kw = val / 1000
|
||||
info.mw = val / 1000000
|
||||
info.gw = val / 1000000000
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">功率单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">瓦(W)</el-text>
|
||||
<el-input
|
||||
v-model="info.w"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('w')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫瓦(mW)</el-text>
|
||||
<el-input
|
||||
v-model="info.mmw"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mmw')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">千瓦(kW)</el-text>
|
||||
<el-input
|
||||
v-model="info.kw"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('kw')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">兆瓦(MW)</el-text>
|
||||
<el-input
|
||||
v-model="info.mw"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mw')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">亿瓦(GW)</el-text>
|
||||
<el-input
|
||||
v-model="info.gw"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('gw')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,193 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const info: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
pa: '',
|
||||
hpa: '',
|
||||
kpa: '',
|
||||
mpa: '',
|
||||
bar: '',
|
||||
torr: '',
|
||||
psi: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in info) {
|
||||
info[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成帕
|
||||
switch (key) {
|
||||
case 'pa':
|
||||
info.pa = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'hpa':
|
||||
info.pa = parseFloat(info[key] as string) * 100
|
||||
break;
|
||||
case 'kpa':
|
||||
info.pa = parseFloat(info[key] as string) * 1000
|
||||
break;
|
||||
case 'mpa':
|
||||
info.pa = parseFloat(info[key] as string) * 1000000
|
||||
break;
|
||||
case 'bar':
|
||||
info.pa = parseFloat(info[key] as string) * 100000
|
||||
break;
|
||||
case 'torr':
|
||||
info.pa = parseFloat(info[key] as string) * 133.322368
|
||||
break;
|
||||
case 'psi':
|
||||
info.pa = parseFloat(info[key] as string) * 6894.76
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//转换成其他单位
|
||||
let val = info.pa as number
|
||||
info.hpa = val / 100
|
||||
info.kpa = val / 1000
|
||||
info.mpa = val / 1000000
|
||||
info.bar = val / 100000
|
||||
info.torr = val / 133.322368
|
||||
info.psi = val / 6894.76
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">压力单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">帕(Pa)</el-text>
|
||||
<el-input
|
||||
v-model="info.pa"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('pa')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">百帕(hPa)</el-text>
|
||||
<el-input
|
||||
v-model="info.hpa"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('hpa')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">千帕(kPa)</el-text>
|
||||
<el-input
|
||||
v-model="info.kpa"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('kpa')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">兆帕(MPa)</el-text>
|
||||
<el-input
|
||||
v-model="info.mpa"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mpa')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">巴(bar)</el-text>
|
||||
<el-input
|
||||
v-model="info.bar"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('bar')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">托(torr)</el-text>
|
||||
<el-input
|
||||
v-model="info.torr"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('torr')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text" style="font-size: 0.8rem;;">磅力/平方英寸(psi)</el-text>
|
||||
<el-input
|
||||
v-model="info.psi"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('psi')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,155 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const info: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
c: '',
|
||||
f: '',
|
||||
k: '',
|
||||
re: '',
|
||||
r: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in info) {
|
||||
info[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成摄氏度
|
||||
switch (key) {
|
||||
case 'c':
|
||||
info.c = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'f':
|
||||
info.c = parseFloat(info[key] as string) * -17.2222222
|
||||
break;
|
||||
case 'k':
|
||||
info.c = parseFloat(info[key] as string) * -272.15
|
||||
break;
|
||||
case 're':
|
||||
info.c = parseFloat(info[key] as string) * 1.25
|
||||
break;
|
||||
case 'r':
|
||||
info.c = parseFloat(info[key] as string) * -272.5944444
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//转换成其他单位
|
||||
let val = info.c as number
|
||||
info.f = 32 + (val * 1.8)
|
||||
info.k = 273.15 + val
|
||||
info.re = val / 1.25
|
||||
info.r = (val + 273.15) * 1.8
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">国际温度单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">摄氏度(℃)</el-text>
|
||||
<el-input
|
||||
v-model="info.c"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('c')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">华氏度(℉)</el-text>
|
||||
<el-input
|
||||
v-model="info.f"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('f')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">开氏度(K)</el-text>
|
||||
<el-input
|
||||
v-model="info.k"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('k')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">列氏度(°Re)</el-text>
|
||||
<el-input
|
||||
v-model="info.re"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('re')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">兰氏度(°R)</el-text>
|
||||
<el-input
|
||||
v-model="info.r"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('r')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,210 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const info: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
year: '',
|
||||
month: '',
|
||||
d: '',
|
||||
h: '',
|
||||
min: '',
|
||||
s: '',
|
||||
ms: '',
|
||||
week: ''
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in info) {
|
||||
info[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
//转换成秒
|
||||
switch (key) {
|
||||
case 'year':
|
||||
info.s = parseFloat(info[key] as string) * 31536000
|
||||
break;
|
||||
case 'month':
|
||||
info.s = parseFloat(info[key] as string) * 2629800
|
||||
break;
|
||||
case 'd':
|
||||
info.s = parseFloat(info[key] as string) * 86400
|
||||
break;
|
||||
case 'h':
|
||||
info.s = parseFloat(info[key] as string) * 3600
|
||||
break;
|
||||
case 'min':
|
||||
info.s = parseFloat(info[key] as string) * 60
|
||||
break;
|
||||
case 's':
|
||||
info.s = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'ms':
|
||||
info.s = parseFloat(info[key] as string) * 0.001
|
||||
break;
|
||||
case 'week':
|
||||
info.s = parseFloat(info[key] as string) * 604800
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//转换成其他单位
|
||||
let val = info.s as number
|
||||
info.year = val / 31536000
|
||||
info.month = info.year * 12
|
||||
info.d = val / 86400
|
||||
info.h = val / 3600
|
||||
info.min = val / 60
|
||||
info.ms = val / 0.001
|
||||
info.week = val / 604800
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">国际时间单位</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">年(year)</el-text>
|
||||
<el-input
|
||||
v-model="info.year"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('year')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">月(month)</el-text>
|
||||
<el-input
|
||||
v-model="info.month"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('month')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">日(d)</el-text>
|
||||
<el-input
|
||||
v-model="info.d"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('d')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">时(h)</el-text>
|
||||
<el-input
|
||||
v-model="info.h"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('h')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">分(min)</el-text>
|
||||
<el-input
|
||||
v-model="info.min"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('min')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">秒(s)</el-text>
|
||||
<el-input
|
||||
v-model="info.s"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('s')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫秒(ms)</el-text>
|
||||
<el-input
|
||||
v-model="info.ms"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('ms')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">周(week)</el-text>
|
||||
<el-input
|
||||
v-model="info.week"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('week')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,76 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
import Length from './Length.vue'
|
||||
import Area from './Area.vue'
|
||||
import Heat from './Heat.vue'
|
||||
import Power from './Power.vue'
|
||||
import Pressure from './Pressure.vue'
|
||||
import Temperature from './Temperature.vue'
|
||||
import Time from './Time.vue'
|
||||
import Weight from './Weight.vue'
|
||||
const info = reactive({
|
||||
title: "单位转换",
|
||||
activeName: 'lengthTab',
|
||||
})
|
||||
|
||||
const handleClick = () => {
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
<div>
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<el-tabs v-model="info.activeName" class="demo-tabs" @tab-click="handleClick">
|
||||
<el-tab-pane label="长度" name="lengthTab">
|
||||
<Length/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="面积" name="areaTab">
|
||||
<Area/>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="体积" name="lengthTab">体积</el-tab-pane> -->
|
||||
<el-tab-pane label="重量" name="weightTab">
|
||||
<Weight/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="时间" name="timeTab">
|
||||
<Time/>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="角度" name="fourth">角度</el-tab-pane> -->
|
||||
<!-- <el-tab-pane label="速度" name="fourth">速度</el-tab-pane> -->
|
||||
<el-tab-pane label="温度" name="temperatureTab">
|
||||
<Temperature/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="压力" name="pressureTab">
|
||||
<Pressure/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="热量" name="heatTab">
|
||||
<Heat/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="功率" name="powerTab">
|
||||
<Power/>
|
||||
</el-tab-pane>
|
||||
<!-- <el-tab-pane label="字节" name="fourth">字节</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 45%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 5rem/* 80px */;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,447 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const info: {
|
||||
[key: string]: string|number
|
||||
} = reactive({
|
||||
t: '',
|
||||
kg: '',
|
||||
g: '',
|
||||
mg: '',
|
||||
|
||||
jin: '',
|
||||
liang: '',
|
||||
qian: '',
|
||||
dan: '',
|
||||
|
||||
eng_lb: '',
|
||||
eng_oz: '',
|
||||
eng_st: '',
|
||||
eng_cwt: '',
|
||||
eng_dr: '',
|
||||
eng_gr: '',
|
||||
eng_lt: '',
|
||||
|
||||
eng_lbt: '',
|
||||
eng_ozt: '',
|
||||
eng_grain: '',
|
||||
eng_dwt: '',
|
||||
})
|
||||
|
||||
//clear
|
||||
const clear = () => {
|
||||
for (let item in info) {
|
||||
info[item] = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换
|
||||
* @param type
|
||||
*/
|
||||
const tran = (key: string) => {
|
||||
switch (key) {
|
||||
case 't':
|
||||
info.g = parseFloat(info[key] as string) * 1000000
|
||||
break;
|
||||
case 'kg':
|
||||
info.g = parseFloat(info[key] as string) * 1000
|
||||
break;
|
||||
case 'g':
|
||||
info.g = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'mg':
|
||||
info.g = parseFloat(info[key] as string) * 0.001
|
||||
break;
|
||||
|
||||
case 'jin':
|
||||
info.g = parseFloat(info[key] as string) * 500
|
||||
break;
|
||||
case 'liang':
|
||||
info.g = parseFloat(info[key] as string) * 50
|
||||
break;
|
||||
case 'qian':
|
||||
info.g = parseFloat(info[key] as string) * 5
|
||||
break;
|
||||
case 'dan':
|
||||
info.g = parseFloat(info[key] as string) * 50000
|
||||
break;
|
||||
|
||||
case 'eng_lb':
|
||||
info.g = parseFloat(info[key] as string) * 453.59237
|
||||
info.eng_lb = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'eng_oz':
|
||||
info.g = parseFloat(info[key] as string) * 28.3495231
|
||||
info.eng_lb = parseFloat(info[key] as string) * 0.0625
|
||||
break;
|
||||
case 'eng_st':
|
||||
info.g = parseFloat(info[key] as string) * 6350.29318
|
||||
info.eng_lb = parseFloat(info[key] as string) * 14
|
||||
break;
|
||||
case 'eng_cwt':
|
||||
info.g = parseFloat(info[key] as string) * 50802.34544
|
||||
info.eng_lb = parseFloat(info[key] as string) * 112
|
||||
break;
|
||||
case 'eng_dr':
|
||||
info.g = parseFloat(info[key] as string) * 1.7718452
|
||||
info.eng_lb = parseFloat(info[key] as string) * 0.0039063
|
||||
break;
|
||||
case 'eng_gr':
|
||||
info.g = parseFloat(info[key] as string) * 0.0647989
|
||||
info.eng_lb = parseFloat(info[key] as string) * 0.0001429
|
||||
break;
|
||||
case 'eng_lt':
|
||||
info.g = parseFloat(info[key] as string) * 1016046.9088
|
||||
info.eng_lb = parseFloat(info[key] as string) * 2240
|
||||
break;
|
||||
|
||||
case 'eng_lbt':
|
||||
info.g = parseFloat(info[key] as string) * 373.2417216
|
||||
info.eng_lbt = parseFloat(info[key] as string) * 1
|
||||
break;
|
||||
case 'eng_ozt':
|
||||
info.g = parseFloat(info[key] as string) * 31.1034768
|
||||
info.eng_lbt = parseFloat(info[key] as string) * 0.0833333
|
||||
break;
|
||||
case 'eng_grain':
|
||||
info.g = parseFloat(info[key] as string) * 0.0647989
|
||||
info.eng_lbt = parseFloat(info[key] as string) * 0.0001736
|
||||
break;
|
||||
case 'eng_dwt':
|
||||
info.g = parseFloat(info[key] as string) * 1.5551738
|
||||
info.eng_lbt = parseFloat(info[key] as string) * 0.0041667
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
//转换成其他单位
|
||||
let val = info.g as number
|
||||
//英制 - 常衡制
|
||||
let engLbVal = val / 453.59237
|
||||
if (['tradition_mu', 'tradition_fen', 'tradition_mill', 'tradition_hao'].includes(key)) {
|
||||
engLbVal = (info.eng_lb == '' ? engLbVal : info.eng_lb) as number
|
||||
}
|
||||
//英制 - 金衡制
|
||||
let engLbtVal = val / 373.2417216
|
||||
if (['eng_lbt', 'eng_ozt', 'eng_grain', 'eng_dwt'].includes(key)) {
|
||||
engLbtVal = (info.eng_lbt == '' ? engLbtVal : info.eng_lbt) as number
|
||||
}
|
||||
info.t = val * 0.000001
|
||||
info.kg = val * 0.001
|
||||
info.mg = val * 1000
|
||||
|
||||
info.jin = val * 0.002
|
||||
info.liang = val * 0.02
|
||||
info.qian = val * 0.2
|
||||
info.dan = val * 0.00002
|
||||
|
||||
info.eng_lb = engLbVal * 1
|
||||
info.eng_oz = engLbVal * 16
|
||||
info.eng_st = engLbVal * 0.0714286
|
||||
info.eng_cwt = engLbVal * 0.0089286
|
||||
info.eng_dr = engLbVal * 256
|
||||
info.eng_gr = engLbVal * 7000
|
||||
info.eng_lt = engLbVal * 0.0004464
|
||||
|
||||
info.eng_lbt = engLbtVal * 1
|
||||
info.eng_ozt = engLbtVal * 12
|
||||
info.eng_grain = engLbtVal * 5760
|
||||
info.eng_dwt = engLbtVal * 240
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-divider content-position="left">国际重量单位(公制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">吨(t)</el-text>
|
||||
<el-input
|
||||
v-model="info.t"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('t')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">千克/公斤(kg)</el-text>
|
||||
<el-input
|
||||
v-model="info.kg"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('kg')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">克(g)</el-text>
|
||||
<el-input
|
||||
v-model="info.g"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('g')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">毫克(mg)</el-text>
|
||||
<el-input
|
||||
v-model="info.mg"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('mg')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ----- -->
|
||||
<el-divider content-position="left">中国传统重量单位(市制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">斤</el-text>
|
||||
<el-input
|
||||
v-model="info.jin"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('jin')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">两</el-text>
|
||||
<el-input
|
||||
v-model="info.liang"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('liang')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">钱</el-text>
|
||||
<el-input
|
||||
v-model="info.qian"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('qian')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">担</el-text>
|
||||
<el-input
|
||||
v-model="info.dan"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('dan')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">英制重量单位(常衡制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">磅(lb)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_lb"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_lb')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">盎司(oz)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_oz"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_oz')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英石(st)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_st"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_st')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英担(cwt)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_cwt"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_cwt')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">打兰(dr)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_dr"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_dr')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">格令(gr)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_gr"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_gr')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">长吨(lt)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_lt"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_lt')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">英制重量单位(金衡制)</el-divider>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">磅(lbt)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_lbt"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_lbt')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">盎司(ozt)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_ozt"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_ozt')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<!-- group -->
|
||||
<div class="custom-box">
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">格令</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_grain"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_grain')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="custom-box-single">
|
||||
<el-text class="custom-box-text">英钱(dwt)</el-text>
|
||||
<el-input
|
||||
v-model="info.eng_dwt"
|
||||
placeholder=""
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="tran('eng_dwt')">转换</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-full text-center">
|
||||
<el-button type="primary" @click="clear">清除全部</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.custom-box{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.75rem/* 12px */;
|
||||
}
|
||||
.custom-box-single{
|
||||
display: flex;
|
||||
width: 50%;
|
||||
}
|
||||
.custom-box-text{
|
||||
text-align: center;
|
||||
width: 10rem;
|
||||
}
|
||||
</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 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<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>
|
||||
@@ -0,0 +1,104 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
// import { copy } from '@/utils/string'
|
||||
import { Jh_getTimeStamp } from '@/utils/time'
|
||||
import { isUrl } from '@/utils/url'
|
||||
import { autoDown, getFileExtension } from '@/utils/file'
|
||||
import { useToolsStore } from '@/store/modules/tools'
|
||||
import { ElMessage } from 'element-plus'
|
||||
const info = reactive({
|
||||
title: "网站favicon获取",
|
||||
})
|
||||
|
||||
//store
|
||||
const toolsStore = useToolsStore()
|
||||
|
||||
const searchUrl = ref()
|
||||
|
||||
const getWebInfo = async () => {
|
||||
try {
|
||||
if (isUrl(searchUrl.value)) {
|
||||
//有效的域名
|
||||
if (!searchUrl.value.includes('http://') && !searchUrl.value.includes('https://')) {
|
||||
searchUrl.value = 'http://' + searchUrl.value;
|
||||
}
|
||||
const urlObj = new URL(searchUrl.value)
|
||||
// webInfo.link = urlObj.hostname
|
||||
searchUrl.value = urlObj.hostname
|
||||
|
||||
await toolsStore.getWebInfo({
|
||||
link: searchUrl.value
|
||||
})
|
||||
} else {
|
||||
//无效的域名
|
||||
ElMessage({
|
||||
message: "无效的域名或地址",
|
||||
type: "error",
|
||||
duration: 1500
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching site info:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const down = () => {
|
||||
autoDown(toolsStore.webInfo.logo, Jh_getTimeStamp() + '.' + getFileExtension(toolsStore.webInfo.logo))
|
||||
}
|
||||
|
||||
//copy
|
||||
// const copyRes = async (resStr: string) => {
|
||||
// copy(resStr)
|
||||
// }
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-input
|
||||
v-model="searchUrl"
|
||||
style="max-width: 600px"
|
||||
placeholder="请输入域名或地址"
|
||||
class="input-with-select"
|
||||
size="large"
|
||||
>
|
||||
<template #append>
|
||||
<el-button type="primary" @click="getWebInfo">获取</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div>
|
||||
<ul>
|
||||
<li class="mt-3">
|
||||
<el-text><b>域名: </b></el-text>
|
||||
<el-link :href="'https://' + searchUrl" type="primary" target="_blank">{{ searchUrl }}</el-link>
|
||||
</li>
|
||||
<li class="mt-3 flex">
|
||||
<el-text><b>logo: </b></el-text>
|
||||
<div v-if="toolsStore.webInfo.logo" class="ml-2 flex items-center">
|
||||
<el-image :src="toolsStore.webInfo.logo" class="h-12"></el-image>
|
||||
<el-button class="ml-3" link type="primary" @click="down">下载</el-button>
|
||||
</div>
|
||||
</li>
|
||||
<li class="mt-3 flex">
|
||||
<el-text><b>标题: </b>{{ toolsStore.webInfo.title }}</el-text>
|
||||
</li>
|
||||
<li class="mt-3 flex">
|
||||
<el-text><b>关键词: </b> {{ toolsStore.webInfo.keywords }}</el-text>
|
||||
</li>
|
||||
<li class="mt-3 flex">
|
||||
<el-text><b>描述: </b> {{ toolsStore.webInfo.desc }}</el-text>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
import { reactive } from 'vue'
|
||||
import DetailHeader from '@/components/Layout/DetailHeader/DetailHeader.vue'
|
||||
|
||||
const info = reactive({
|
||||
title: "在线字数统计",
|
||||
content: '',
|
||||
tranRes: '',
|
||||
chinaCharNum: 0,
|
||||
letterNum: 0,
|
||||
punctuationHalfNum: 0,
|
||||
punctuationFullNum: 0,
|
||||
numberNum: 0,
|
||||
emptyNum: 0,
|
||||
sum: 0,
|
||||
})
|
||||
|
||||
//内容发生变化,重新统计
|
||||
const changeContent = () => {
|
||||
//汉字
|
||||
info.chinaCharNum = (info.content.match(/[\u4e00-\u9fa5]/g) || []).length;
|
||||
//字母
|
||||
info.letterNum = (info.content.match(/[a-zA-Z]/g) || []).length;
|
||||
//符号 - 半角
|
||||
info.punctuationHalfNum = (info.content.match(/[!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]/g) || []).length;
|
||||
//符号 - 全角
|
||||
info.punctuationFullNum = (info.content.match(/[\u3002|\uff1f|\uff01|\uff0c|\u3001|\uff1b|\uff1a|\u201c|\u201d|\u2018|\u2019|\uff08|\uff09|\u300a|\u300b|\u3008|\u3009|\u3010|\u3011|\u300e|\u300f|\u300c|\u300d|\ufe43|\ufe44|\u3014|\u3015|\u2026|\u2014|\uff5e|\ufe4f|\uffe5]/g) || []).length;
|
||||
//数字
|
||||
info.numberNum = (info.content.match(/[\d+]/g) || []).length;
|
||||
//空白字符
|
||||
info.emptyNum = (info.content.match(/[\s*]/g) || []).length;
|
||||
//合计
|
||||
info.sum = info.chinaCharNum + info.letterNum + info.punctuationHalfNum + info.punctuationFullNum + info.numberNum + info.emptyNum;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col mt-3 flex-1">
|
||||
<DetailHeader :title="info.title"></DetailHeader>
|
||||
|
||||
<div class="p-4 rounded-2xl bg-white">
|
||||
<div>
|
||||
<el-input type="textarea" :rows="16" v-model="info.content" @input="changeContent"></el-input>
|
||||
</div>
|
||||
<div class="mt-3 min-h-md bg-gray-100 p-3 mb-3 flex">
|
||||
<div><el-text>总字数:{{ info.sum }}</el-text></div>
|
||||
<div class="ml-3"><el-text>汉字:{{ info.chinaCharNum }}</el-text></div>
|
||||
<div class="ml-3"><el-text>字母:{{ info.letterNum }}</el-text></div>
|
||||
<div class="ml-3"><el-text>数字:{{ info.numberNum }}</el-text></div>
|
||||
<div class="ml-3"><el-text>标点符号半角:{{ info.punctuationHalfNum }}</el-text></div>
|
||||
<div class="ml-3"><el-text>标点符号全角:{{ info.punctuationFullNum }}</el-text></div>
|
||||
<div class="ml-3"><el-text>空白字符:{{ info.emptyNum }}</el-text></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user