init
This commit is contained in:
15
.eslintrc.cjs
Normal file
15
.eslintrc.cjs
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint-env node */
|
||||
require('@rushstack/eslint-patch/modern-module-resolution')
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
'extends': [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'@vue/eslint-config-typescript',
|
||||
'@vue/eslint-config-prettier'
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
}
|
||||
}
|
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
1
.prettierrc.json
Normal file
1
.prettierrc.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||
}
|
46
README.md
Normal file
46
README.md
Normal file
@ -0,0 +1,46 @@
|
||||
# gen_short_link
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
||||
|
||||
## Type Support for `.vue` Imports in TS
|
||||
|
||||
TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types.
|
||||
|
||||
If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps:
|
||||
|
||||
1. Disable the built-in TypeScript Extension
|
||||
1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette
|
||||
2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)`
|
||||
2. Reload the VSCode window by running `Developer: Reload Window` from the command palette.
|
||||
|
||||
## Customize configuration
|
||||
|
||||
See [Vite Configuration Reference](https://vitejs.dev/config/).
|
||||
|
||||
## Project Setup
|
||||
|
||||
```sh
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Compile and Hot-Reload for Development
|
||||
|
||||
```sh
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### Type-Check, Compile and Minify for Production
|
||||
|
||||
```sh
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Lint with [ESLint](https://eslint.org/)
|
||||
|
||||
```sh
|
||||
pnpm lint
|
||||
```
|
13
index.html
Normal file
13
index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
41
package.json
Normal file
41
package.json
Normal file
@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "gen_short_link",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "run-p type-check build-only",
|
||||
"preview": "vite preview",
|
||||
"build-only": "vite build",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.0.10",
|
||||
"axios": "^1.3.2",
|
||||
"element-plus": "^2.2.30",
|
||||
"modern-normalize": "^1.1.0",
|
||||
"qrcode.vue": "^3.3.3",
|
||||
"vue": "^3.2.45",
|
||||
"vue-router": "^4.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/node": "^18.11.12",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint-plugin-vue": "^9.3.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.58.0",
|
||||
"tailwindcss": "^3.2.6",
|
||||
"typescript": "~4.7.4",
|
||||
"vite": "^4.0.0",
|
||||
"vue-tsc": "^1.0.12"
|
||||
}
|
||||
}
|
2822
pnpm-lock.yaml
generated
Normal file
2822
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
75
src/App.vue
Normal file
75
src/App.vue
Normal file
@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
// export default defineComponent({
|
||||
// components: { Gen_short_link }
|
||||
//
|
||||
// });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <GenShortLink class="w-full h-full"></GenShortLink>-->
|
||||
<RouterView />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/*header {*/
|
||||
/* line-height: 1.5;*/
|
||||
/* max-height: 100vh;*/
|
||||
/*}*/
|
||||
|
||||
/*.logo {*/
|
||||
/* display: block;*/
|
||||
/* margin: 0 auto 2rem;*/
|
||||
/*}*/
|
||||
|
||||
/*nav {*/
|
||||
/* width: 100%;*/
|
||||
/* font-size: 12px;*/
|
||||
/* text-align: center;*/
|
||||
/* margin-top: 2rem;*/
|
||||
/*}*/
|
||||
|
||||
/*nav a.router-link-exact-active {*/
|
||||
/* color: var(--color-text);*/
|
||||
/*}*/
|
||||
|
||||
/*nav a.router-link-exact-active:hover {*/
|
||||
/* background-color: transparent;*/
|
||||
/*}*/
|
||||
|
||||
/*nav a {*/
|
||||
/* display: inline-block;*/
|
||||
/* padding: 0 1rem;*/
|
||||
/* border-left: 1px solid var(--color-border);*/
|
||||
/*}*/
|
||||
|
||||
/*nav a:first-of-type {*/
|
||||
/* border: 0;*/
|
||||
/*}*/
|
||||
|
||||
/*@media (min-width: 1024px) {*/
|
||||
/* header {*/
|
||||
/* display: flex;*/
|
||||
/* place-items: center;*/
|
||||
/* padding-right: calc(var(--section-gap) / 2);*/
|
||||
/* }*/
|
||||
|
||||
/* .logo {*/
|
||||
/* margin: 0 2rem 0 0;*/
|
||||
/* }*/
|
||||
|
||||
/* header .wrapper {*/
|
||||
/* display: flex;*/
|
||||
/* place-items: flex-start;*/
|
||||
/* flex-wrap: wrap;*/
|
||||
/* }*/
|
||||
|
||||
/* nav {*/
|
||||
/* text-align: left;*/
|
||||
/* margin-left: -1rem;*/
|
||||
/* font-size: 1rem;*/
|
||||
|
||||
/* padding: 1rem 0;*/
|
||||
/* margin-top: 1rem;*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
</style>
|
38
src/api/shortlink.ts
Normal file
38
src/api/shortlink.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import request from "@/utils/request";
|
||||
import type { ShortLink } from "@/models/shortLink";
|
||||
import type { LogListParams } from "@/models/LogListParams";
|
||||
|
||||
export const listShortLink = (params: { pageNum: number; pageSize: number }) =>
|
||||
request({
|
||||
method: "get",
|
||||
url: "/shortLink/list",
|
||||
params
|
||||
});
|
||||
|
||||
export const addShortLink = (data: ShortLink) =>
|
||||
request({
|
||||
method: "post",
|
||||
url: "/shortLink",
|
||||
data
|
||||
});
|
||||
export const updateShortLink = (data: ShortLink) =>
|
||||
request({
|
||||
method: "put",
|
||||
url: "/shortLink",
|
||||
data
|
||||
});
|
||||
export const getDetailById = (id: number) =>
|
||||
request({ method: "get", url: `/shortLink/getInfo/${id}` });
|
||||
|
||||
export const deleteShortLink = (ids: string) =>
|
||||
request({
|
||||
url: `/shortLink/${ids}`,
|
||||
method: "delete"
|
||||
});
|
||||
|
||||
export const listLogList = (params: LogListParams) =>
|
||||
request({
|
||||
url: `/shortLink/getShortLinkLogList`,
|
||||
method: "get",
|
||||
params
|
||||
});
|
75
src/assets/base.css
Normal file
75
src/assets/base.css
Normal file
@ -0,0 +1,75 @@
|
||||
/* color palette from <https://github.com/vuejs/theme> */
|
||||
:root {
|
||||
--vt-c-white: #ffffff;
|
||||
--vt-c-white-soft: #f8f8f8;
|
||||
--vt-c-white-mute: #f2f2f2;
|
||||
|
||||
--vt-c-black: #181818;
|
||||
--vt-c-black-soft: #222222;
|
||||
--vt-c-black-mute: #282828;
|
||||
|
||||
--vt-c-indigo: #2c3e50;
|
||||
|
||||
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
|
||||
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
|
||||
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
|
||||
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
|
||||
|
||||
--vt-c-text-light-1: var(--vt-c-indigo);
|
||||
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
|
||||
--vt-c-text-dark-1: var(--vt-c-white);
|
||||
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
|
||||
}
|
||||
|
||||
/* semantic color variables for this project */
|
||||
:root {
|
||||
--color-background: var(--vt-c-white);
|
||||
--color-background-soft: var(--vt-c-white-soft);
|
||||
--color-background-mute: var(--vt-c-white-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-light-2);
|
||||
--color-border-hover: var(--vt-c-divider-light-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-light-1);
|
||||
--color-text: var(--vt-c-text-light-1);
|
||||
|
||||
--section-gap: 160px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-background: var(--vt-c-black);
|
||||
--color-background-soft: var(--vt-c-black-soft);
|
||||
--color-background-mute: var(--vt-c-black-mute);
|
||||
|
||||
--color-border: var(--vt-c-divider-dark-2);
|
||||
--color-border-hover: var(--vt-c-divider-dark-1);
|
||||
|
||||
--color-heading: var(--vt-c-text-dark-1);
|
||||
--color-text: var(--vt-c-text-dark-2);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
color: var(--color-text);
|
||||
background: var(--color-background);
|
||||
transition: color 0.5s, background-color 0.5s;
|
||||
line-height: 1.6;
|
||||
font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-size: 15px;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
540
src/assets/iconfont/demo.css
Normal file
540
src/assets/iconfont/demo.css
Normal file
@ -0,0 +1,540 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url("https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834");
|
||||
src: url("https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix")
|
||||
format("embedded-opentype"),
|
||||
url("https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834")
|
||||
format("woff"),
|
||||
url("https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834")
|
||||
format("truetype"),
|
||||
url("https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont")
|
||||
format("svg");
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown > p,
|
||||
.markdown > blockquote,
|
||||
.markdown > .highlight,
|
||||
.markdown > ol,
|
||||
.markdown > ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul > li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown > ul li,
|
||||
.markdown blockquote ul > li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown > ul li p,
|
||||
.markdown > ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol > li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown > ol li,
|
||||
.markdown blockquote ol > li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown > table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown > table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown > table th,
|
||||
.markdown > table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown > table th {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown > br,
|
||||
.markdown > p > br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: 0.5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre) > code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre) > code[class*="language-"] {
|
||||
padding: 0.1em;
|
||||
border-radius: 0.3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, 0.5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #dd4a68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
253
src/assets/iconfont/demo_index.html
Normal file
253
src/assets/iconfont/demo_index.html
Normal file
@ -0,0 +1,253 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>iconfont Demo</title>
|
||||
<link
|
||||
rel="shortcut icon"
|
||||
href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"
|
||||
type="image/x-icon"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/svg+xml"
|
||||
href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css"
|
||||
/>
|
||||
<link rel="stylesheet" href="demo.css" />
|
||||
<link rel="stylesheet" href="iconfont.css" />
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
<style>
|
||||
.main .logo {
|
||||
margin-top: 0;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.main .logo .sub-title {
|
||||
margin-left: 0.5em;
|
||||
font-size: 22px;
|
||||
color: #fff;
|
||||
background: linear-gradient(-45deg, #3967ff, #b500fe);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo">
|
||||
<a
|
||||
href="https://www.iconfont.cn/"
|
||||
title="iconfont 首页"
|
||||
target="_blank"
|
||||
>
|
||||
<img
|
||||
width="200"
|
||||
src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg"
|
||||
/>
|
||||
</a>
|
||||
</h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a
|
||||
href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=3892861"
|
||||
target="_blank"
|
||||
class="nav-more"
|
||||
>查看项目</a
|
||||
>
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block">
|
||||
<ul class="icon_lists dib-box">
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">qrcode</div>
|
||||
<div class="code-name">&#xe71c;</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr />
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>
|
||||
注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol
|
||||
引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">
|
||||
第一步:拷贝项目下面生成的 <code>@font-face</code>
|
||||
</h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1676268957528') format('woff2'),
|
||||
url('iconfont.woff?t=1676268957528') format('woff'),
|
||||
url('iconfont.ttf?t=1676268957528') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>
|
||||
"iconfont" 是你项目下的
|
||||
font-family。可以通过编辑项目查看,默认是 "iconfont"。
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-qrcode"></span>
|
||||
<div class="name">qrcode</div>
|
||||
<div class="code-name">.icon-qrcode</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode
|
||||
书写不直观,语意不明确的问题。
|
||||
</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>
|
||||
相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon
|
||||
是什么。
|
||||
</li>
|
||||
<li>
|
||||
因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class
|
||||
里面的 Unicode 引用。
|
||||
</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">
|
||||
第一步:引入项目下面生成的 fontclass 代码:
|
||||
</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>
|
||||
" iconfont" 是你项目下的
|
||||
font-family。可以通过编辑项目查看,默认是 "iconfont"。
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-qrcode"></use>
|
||||
</svg>
|
||||
<div class="name">qrcode</div>
|
||||
<div class="code-name">#icon-qrcode</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr />
|
||||
|
||||
<p>
|
||||
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a
|
||||
href=""
|
||||
>文章</a
|
||||
>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
|
||||
</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>
|
||||
通过一些技巧,支持像字体那样,通过 <code>font-size</code>,
|
||||
<code>color</code> 来调整样式。
|
||||
</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$(".tab-container .content:first").show();
|
||||
|
||||
$("#tabs li").click(function (e) {
|
||||
var tabContent = $(".tab-container .content");
|
||||
var index = $(this).index();
|
||||
|
||||
if ($(this).hasClass("active")) {
|
||||
return;
|
||||
} else {
|
||||
$("#tabs li").removeClass("active");
|
||||
$(this).addClass("active");
|
||||
|
||||
tabContent.hide().eq(index).fadeIn();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
18
src/assets/iconfont/iconfont.css
Normal file
18
src/assets/iconfont/iconfont.css
Normal file
@ -0,0 +1,18 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 3892861 */
|
||||
src: url("iconfont.woff2?t=1676268957528") format("woff2"),
|
||||
url("iconfont.woff?t=1676268957528") format("woff"),
|
||||
url("iconfont.ttf?t=1676268957528") format("truetype");
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-qrcode:before {
|
||||
content: "\e71c";
|
||||
}
|
69
src/assets/iconfont/iconfont.js
Normal file
69
src/assets/iconfont/iconfont.js
Normal file
@ -0,0 +1,69 @@
|
||||
(window._iconfont_svg_string_3892861 =
|
||||
'<svg><symbol id="icon-qrcode" viewBox="0 0 1024 1024"><path d="M224 416.096V224l192-0.096 0.096 192.096L224 416.096zM416.096 160H223.904A64 64 0 0 0 160 223.904v192.192A64 64 0 0 0 223.904 480h192.192A64 64 0 0 0 480 416.096V223.904A64 64 0 0 0 416.096 160zM224 800.096V608l192-0.096 0.096 192.096L224 800.096zM416.096 544H223.904A64 64 0 0 0 160 607.904v192.192A64 64 0 0 0 223.904 864h192.192A64 64 0 0 0 480 800.096v-192.192A64 64 0 0 0 416.096 544zM608 416.096V224l192-0.096 0.096 192.096-192.096 0.096zM800.096 160h-192.192A64 64 0 0 0 544 223.904v192.192A64 64 0 0 0 607.904 480h192.192A64 64 0 0 0 864 416.096V223.904A64 64 0 0 0 800.096 160zM704 608a32 32 0 0 0-32 32v192a32 32 0 0 0 64 0v-192a32 32 0 0 0-32-32M576 608a32 32 0 0 0-32 32v192a32 32 0 0 0 64 0v-192a32 32 0 0 0-32-32M832 544a32 32 0 0 0-32 32v256a32 32 0 0 0 64 0v-256a32 32 0 0 0-32-32" fill="#3E3A39" ></path></symbol></svg>'),
|
||||
(function (n) {
|
||||
var t = (t = document.getElementsByTagName("script"))[t.length - 1],
|
||||
e = t.getAttribute("data-injectcss"),
|
||||
t = t.getAttribute("data-disable-injectsvg");
|
||||
if (!t) {
|
||||
var o,
|
||||
i,
|
||||
a,
|
||||
d,
|
||||
c,
|
||||
s = function (t, e) {
|
||||
e.parentNode.insertBefore(t, e);
|
||||
};
|
||||
if (e && !n.__iconfont__svg__cssinject__) {
|
||||
n.__iconfont__svg__cssinject__ = !0;
|
||||
try {
|
||||
document.write(
|
||||
"<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>"
|
||||
);
|
||||
} catch (t) {
|
||||
console && console.log(t);
|
||||
}
|
||||
}
|
||||
(o = function () {
|
||||
var t,
|
||||
e = document.createElement("div");
|
||||
(e.innerHTML = n._iconfont_svg_string_3892861),
|
||||
(e = e.getElementsByTagName("svg")[0]) &&
|
||||
(e.setAttribute("aria-hidden", "true"),
|
||||
(e.style.position = "absolute"),
|
||||
(e.style.width = 0),
|
||||
(e.style.height = 0),
|
||||
(e.style.overflow = "hidden"),
|
||||
(e = e),
|
||||
(t = document.body).firstChild
|
||||
? s(e, t.firstChild)
|
||||
: t.appendChild(e));
|
||||
}),
|
||||
document.addEventListener
|
||||
? ~["complete", "loaded", "interactive"].indexOf(document.readyState)
|
||||
? setTimeout(o, 0)
|
||||
: ((i = function () {
|
||||
document.removeEventListener("DOMContentLoaded", i, !1), o();
|
||||
}),
|
||||
document.addEventListener("DOMContentLoaded", i, !1))
|
||||
: document.attachEvent &&
|
||||
((a = o),
|
||||
(d = n.document),
|
||||
(c = !1),
|
||||
r(),
|
||||
(d.onreadystatechange = function () {
|
||||
"complete" == d.readyState &&
|
||||
((d.onreadystatechange = null), l());
|
||||
}));
|
||||
}
|
||||
function l() {
|
||||
c || ((c = !0), a());
|
||||
}
|
||||
function r() {
|
||||
try {
|
||||
d.documentElement.doScroll("left");
|
||||
} catch (t) {
|
||||
return void setTimeout(r, 50);
|
||||
}
|
||||
l();
|
||||
}
|
||||
})(window);
|
16
src/assets/iconfont/iconfont.json
Normal file
16
src/assets/iconfont/iconfont.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "3892861",
|
||||
"name": "shortlink",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "7008991",
|
||||
"name": "qrcode",
|
||||
"font_class": "qrcode",
|
||||
"unicode": "e71c",
|
||||
"unicode_decimal": 59164
|
||||
}
|
||||
]
|
||||
}
|
BIN
src/assets/iconfont/iconfont.ttf
Normal file
BIN
src/assets/iconfont/iconfont.ttf
Normal file
Binary file not shown.
BIN
src/assets/iconfont/iconfont.woff
Normal file
BIN
src/assets/iconfont/iconfont.woff
Normal file
Binary file not shown.
BIN
src/assets/iconfont/iconfont.woff2
Normal file
BIN
src/assets/iconfont/iconfont.woff2
Normal file
Binary file not shown.
1
src/assets/logo.svg
Normal file
1
src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
After Width: | Height: | Size: 308 B |
39
src/assets/main.css
Normal file
39
src/assets/main.css
Normal file
@ -0,0 +1,39 @@
|
||||
/*@tailwind base;*/
|
||||
/*@tailwind components;*/
|
||||
/*@tailwind utilities;*/
|
||||
|
||||
/*@import "./base.css";*/
|
||||
|
||||
/*#app {*/
|
||||
/* max-width: 1280px;*/
|
||||
/* margin: 0 auto;*/
|
||||
/* padding: 2rem;*/
|
||||
|
||||
/* font-weight: normal;*/
|
||||
/*}*/
|
||||
|
||||
/*a,*/
|
||||
/*.green {*/
|
||||
/* text-decoration: none;*/
|
||||
/* color: hsla(160, 100%, 37%, 1);*/
|
||||
/* transition: 0.4s;*/
|
||||
/*}*/
|
||||
|
||||
/*@media (hover: hover) {*/
|
||||
/* a:hover {*/
|
||||
/* background-color: hsla(160, 100%, 37%, 0.2);*/
|
||||
/* }*/
|
||||
/*}*/
|
||||
|
||||
/*@media (min-width: 1024px) {*/
|
||||
/* body {*/
|
||||
/* display: flex;*/
|
||||
/* place-items: center;*/
|
||||
/* }*/
|
||||
|
||||
/* #app {*/
|
||||
/* display: grid;*/
|
||||
/* grid-template-columns: 1fr 1fr;*/
|
||||
/* padding: 0 2rem;*/
|
||||
/* }*/
|
||||
/*}*/
|
24
src/main.ts
Normal file
24
src/main.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { createApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import ElementPlus from "element-plus";
|
||||
import "element-plus/dist/index.css";
|
||||
import "./assets/main.css";
|
||||
import "modern-normalize/modern-normalize.css";
|
||||
// import "@/assets/iconfont/iconfont.js";
|
||||
import "@/assets/iconfont/iconfont.css";
|
||||
// @ts-ignore
|
||||
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
|
||||
|
||||
import * as ElementPlusIconsVue from "@element-plus/icons-vue";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
app.use(router);
|
||||
app.use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
});
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(key, component);
|
||||
}
|
||||
app.mount("#app");
|
7
src/models/LogListParams.ts
Normal file
7
src/models/LogListParams.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export interface LogListParams {
|
||||
urlSuffix: string;
|
||||
pageNum: number;
|
||||
pageSize: number;
|
||||
startTime?: string;
|
||||
endTime?: string;
|
||||
}
|
8
src/models/shortLink.ts
Normal file
8
src/models/shortLink.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface ShortLink {
|
||||
id?: number;
|
||||
jumpUrl?: string;
|
||||
name?: string;
|
||||
urlPrefix?: string;
|
||||
urlSuffix?: string;
|
||||
validityTime?: string;
|
||||
}
|
5
src/models/shortLinkListResp.ts
Normal file
5
src/models/shortLinkListResp.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface ShortLinkListResp {
|
||||
rows: ShortLinkListResp[];
|
||||
total: number;
|
||||
msg: string;
|
||||
}
|
21
src/router/index.ts
Normal file
21
src/router/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import GenShortLink from "@/views/GenShortLink.vue";
|
||||
import AccessRecords from "@/views/AccessRecords.vue";
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{
|
||||
path: "/",
|
||||
name: "ShortLinkList",
|
||||
component: GenShortLink,
|
||||
},
|
||||
{
|
||||
path: "/log-list/:suffix",
|
||||
name: "AccessRecords",
|
||||
component: AccessRecords,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export default router;
|
6
src/utils/errorCode.ts
Normal file
6
src/utils/errorCode.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const errorCode: any = {
|
||||
"401": "认证失败,无法访问系统资源",
|
||||
"403": "当前操作没有权限",
|
||||
"404": "访问资源不存在",
|
||||
default: "系统未知错误,请反馈给管理员",
|
||||
};
|
51
src/utils/request.ts
Normal file
51
src/utils/request.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import axios from "axios";
|
||||
import { ElMessage, ElNotification } from "element-plus";
|
||||
import { errorCode } from "@/utils/errorCode";
|
||||
// 创建axios实例
|
||||
|
||||
const service = axios.create({
|
||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||
// baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
// 超时
|
||||
baseURL: "/dev-api",
|
||||
timeout: 100000,
|
||||
});
|
||||
|
||||
service.interceptors.response.use(
|
||||
(res) => {
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = res.data.code || 200;
|
||||
// 获取错误信息
|
||||
const msg = errorCode[code] || res.data.msg || errorCode["default"];
|
||||
if (code === 500) {
|
||||
ElMessage.error(msg);
|
||||
return Promise.reject(new Error(msg));
|
||||
} else if (code !== 200) {
|
||||
ElNotification.error({
|
||||
title: msg,
|
||||
});
|
||||
return Promise.reject("error");
|
||||
} else {
|
||||
return Promise.resolve(res);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.log("err" + error);
|
||||
let { message } = error;
|
||||
if (message == "Network Error") {
|
||||
message = "后端接口连接异常";
|
||||
} else if (message.includes("timeout")) {
|
||||
message = "系统接口请求超时";
|
||||
} else if (message.includes("Request failed with status code")) {
|
||||
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
||||
}
|
||||
ElMessage.error({ message, duration: 5 * 1000 });
|
||||
// ElMessage({
|
||||
// message: message,
|
||||
// type: "error",
|
||||
// duration: 5 * 1000,
|
||||
// });
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
export default service;
|
122
src/views/AccessRecords.vue
Normal file
122
src/views/AccessRecords.vue
Normal file
@ -0,0 +1,122 @@
|
||||
<template>
|
||||
<div id="app-container">
|
||||
<el-button size="small" @click="back" type="primary" plain icon="back"
|
||||
>返回</el-button
|
||||
>
|
||||
<el-form
|
||||
class="query-form"
|
||||
inline
|
||||
size="small"
|
||||
:model="queryParams"
|
||||
ref="queryParamsRef"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="queryParams.startTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择开始时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="queryParams.endTime"
|
||||
type="datetime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
placeholder="请选择结束时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="search" @click="handleSearch"
|
||||
>搜索
|
||||
</el-button>
|
||||
<el-button type="primary" icon="refresh" @click="resetQuery"
|
||||
>重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="logList" v-loading>
|
||||
<el-table-column label="访问时间" prop="visitTime"></el-table-column>
|
||||
<el-table-column label="访问IP" prop="visitIp"></el-table-column>
|
||||
<el-table-column label="访问地区" prop="visitAddress"></el-table-column>
|
||||
<el-table-column label="访问设备" prop="visitDevice"></el-table-column>
|
||||
<el-table-column label="访问后缀" prop="urlSuffix"></el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
@update:current-page="handleCurrentChange"
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="AccessRecords" lang="ts">
|
||||
import { reactive, ref, toRefs } from "vue";
|
||||
import { listLogList } from "@/api/shortlink";
|
||||
import type { LogListParams } from "@/models/LogListParams";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const logList = ref([]);
|
||||
const total = ref(0);
|
||||
const data = reactive<{ queryParams: LogListParams }>({
|
||||
queryParams: {
|
||||
urlSuffix: route.params.suffix as string,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
});
|
||||
|
||||
const { queryParams } = toRefs(data);
|
||||
const getList = async () => {
|
||||
const resp = await listLogList(queryParams.value);
|
||||
logList.value = resp.data.rows;
|
||||
total.value = resp.data.total;
|
||||
};
|
||||
const queryParamsRef = ref();
|
||||
const resetQuery = () => {
|
||||
queryParams.value = {
|
||||
urlSuffix: route.params.suffix as string,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
if (queryParamsRef.value) {
|
||||
queryParamsRef.value.resetFields();
|
||||
}
|
||||
getList();
|
||||
};
|
||||
const handleSearch = () => {
|
||||
queryParams.value.pageNum = 1;
|
||||
getList();
|
||||
};
|
||||
const handleCurrentChange = (page: number) => {
|
||||
queryParams.value.pageNum = page;
|
||||
getList();
|
||||
};
|
||||
const back = () => {
|
||||
router.push("/");
|
||||
};
|
||||
getList();
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
#app-container {
|
||||
padding: 20px;
|
||||
|
||||
.query-form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
</style>
|
383
src/views/GenShortLink.vue
Normal file
383
src/views/GenShortLink.vue
Normal file
@ -0,0 +1,383 @@
|
||||
<template>
|
||||
<div id="app-container" class="p-3">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="1.5">
|
||||
<el-button @click="handleAdd" type="primary" size="small" icon="plus"
|
||||
>新增
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-button
|
||||
icon="delete"
|
||||
@click="handleDelete()"
|
||||
type="danger"
|
||||
size="small"
|
||||
:disabled="multiple"
|
||||
>批量删除
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-table
|
||||
class="short-link-table"
|
||||
:data="shortLinkList"
|
||||
@selection-change="handleSelectionChange"
|
||||
v-loading
|
||||
>
|
||||
<el-table-column align="center" type="selection" width="50" />
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="名称"
|
||||
prop="name"
|
||||
width="120"
|
||||
></el-table-column>
|
||||
<el-table-column align="center" label="短链接网址" prop="jumpUrl">
|
||||
<template #default="{ row }">
|
||||
<el-row :gutter="5">
|
||||
<el-col :span="18">
|
||||
<div>
|
||||
<a
|
||||
class="short-link"
|
||||
:href="`//${row.urlPrefix}/dlj/${row.urlSuffix}`"
|
||||
>
|
||||
{{ row.urlPrefix }}/dlj/{{ row.urlSuffix }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="jump-url">{{ row.jumpUrl }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-row :gutter="5">
|
||||
<el-col :span="1.5">
|
||||
<i
|
||||
class="iconfont icon-qrcode"
|
||||
:style="{
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="handleShowQrCode(row)"
|
||||
></i>
|
||||
</el-col>
|
||||
<el-col :span="1.5">
|
||||
<el-icon
|
||||
:style="{
|
||||
cursor: 'pointer',
|
||||
}"
|
||||
@click="copyToClipboard(row.jumpUrl)"
|
||||
>
|
||||
<CopyDocument />
|
||||
</el-icon>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="访问次数" width="160" align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="visit-count">
|
||||
<div>
|
||||
<span class="title">今日</span>
|
||||
<span class="count">{{ row.todayVisit }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="title">累计</span>
|
||||
<span class="count">{{ row.historyVisit }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="过期时间"
|
||||
prop="validityTime"
|
||||
></el-table-column>
|
||||
<el-table-column align="center" label="操作">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
@click="handleUpdate(row.id)"
|
||||
link
|
||||
size="small"
|
||||
type="warning"
|
||||
>编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="handleDelete(row.id)"
|
||||
link
|
||||
size="small"
|
||||
type="danger"
|
||||
>删除
|
||||
</el-button>
|
||||
<el-button
|
||||
@click="goLogList(row.urlSuffix)"
|
||||
link
|
||||
size="small"
|
||||
type="primary"
|
||||
>访问记录
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:page-size="queryParams.pageSize"
|
||||
:current-page="queryParams.pageNum"
|
||||
@update:current-page="handleCurrentChange"
|
||||
:total="total"
|
||||
/>
|
||||
</div>
|
||||
<el-dialog v-model="showEditDialog" title="新增短链接">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" :label-width="100">
|
||||
<el-form-item label="链接标题" prop="name">
|
||||
<el-input placeholder="请输入链接标题" v-model="form.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="跳转链接" prop="jumpUrl">
|
||||
<el-input
|
||||
placeholder="请输入跳转链接"
|
||||
v-model="form.jumpUrl"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="URL前缀" prop="urlPrefix">
|
||||
<el-input
|
||||
placeholder="请输入URL前缀"
|
||||
v-model="form.urlPrefix"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有效期" prop="validityTime">
|
||||
<el-date-picker
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
v-model="form.validityTime"
|
||||
type="datetime"
|
||||
placeholder="请选择过期时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="showEditDialog = false">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="showQrCodeDialog" width="320">
|
||||
<qrcode-vue
|
||||
class="qrcode-canvas"
|
||||
:value="qrcodeValue"
|
||||
:size="280"
|
||||
ref="qrcodeRef"
|
||||
:margin="1"
|
||||
></qrcode-vue>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="downloadQrCode"
|
||||
:style="{
|
||||
width: '100%',
|
||||
marginTop: '10px',
|
||||
}"
|
||||
>下载二维码
|
||||
</el-button>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="GenShortLink">
|
||||
import { addShortLink, deleteShortLink, getDetailById, listShortLink, updateShortLink } from "@/api/shortlink";
|
||||
import { reactive, ref, toRefs } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import type { ShortLink } from "@/models/shortLink";
|
||||
|
||||
import QrcodeVue from "qrcode.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const qrcodeRef = ref();
|
||||
const shortLinkList = ref<ShortLink[]>([]);
|
||||
const showEditDialog = ref(false);
|
||||
const editDialogTitle = ref<string>("");
|
||||
const showQrCodeDialog = ref(false);
|
||||
const qrcodeValue = ref("");
|
||||
const qrcodeFileName = ref("");
|
||||
const formRef = ref();
|
||||
const data = reactive<{
|
||||
queryParams: { pageNum: number; pageSize: number };
|
||||
form: ShortLink;
|
||||
rules: any;
|
||||
}>({
|
||||
queryParams: {
|
||||
pageNum: 1,
|
||||
pageSize: 10
|
||||
},
|
||||
form: {},
|
||||
rules: {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入短链接名称",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
jumpUrl: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入短链接",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
urlPrefix: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入URL前缀",
|
||||
trigger: "blur"
|
||||
}
|
||||
],
|
||||
validityTime: [
|
||||
{
|
||||
required: true,
|
||||
message: "请选择过期时间",
|
||||
trigger: "change"
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
const { form, queryParams, rules } = toRefs(data);
|
||||
const total = ref<number>(0);
|
||||
const router = useRouter();
|
||||
const getList = async () => {
|
||||
const resp = await listShortLink(queryParams.value);
|
||||
shortLinkList.value = resp.data.rows;
|
||||
total.value = resp.data.total;
|
||||
};
|
||||
const handleShowQrCode = (data: ShortLink) => {
|
||||
qrcodeValue.value = `${data.urlPrefix}/dlj/${data.urlSuffix}`;
|
||||
qrcodeFileName.value = `${data.urlSuffix}.png`;
|
||||
showQrCodeDialog.value = true;
|
||||
};
|
||||
const goLogList = (suffix: string) => {
|
||||
router.push({
|
||||
path: `/log-list/${suffix}`
|
||||
});
|
||||
};
|
||||
const reset = () => {
|
||||
form.value = {};
|
||||
if (formRef.value) {
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
};
|
||||
// 多选框选中数据
|
||||
const linkIds = ref<number[]>([]);
|
||||
const multiple = ref(true);
|
||||
const single = ref(true);
|
||||
const downloadQrCode = () => {
|
||||
console.log(qrcodeRef.value);
|
||||
const qrcodeCanvas: HTMLCanvasElement | null =
|
||||
document.querySelector(".qrcode-canvas");
|
||||
qrcodeCanvas?.toBlob((blob: Blob | null) => {
|
||||
if (blob != null) {
|
||||
let aLink = document.createElement("a");
|
||||
aLink.download = qrcodeFileName.value;
|
||||
aLink.href = URL.createObjectURL(blob);
|
||||
aLink.click();
|
||||
} else {
|
||||
ElMessage.error("下载二维码失败");
|
||||
}
|
||||
});
|
||||
};
|
||||
const handleSelectionChange = (selection: ShortLink[]) => {
|
||||
linkIds.value = selection.map((item: ShortLink) => item.id!);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
const copyToClipboard = (data: string) => {
|
||||
navigator.clipboard.writeText(data);
|
||||
ElMessage.success("短链接已拷贝到剪贴板");
|
||||
};
|
||||
const handleAdd = () => {
|
||||
reset();
|
||||
editDialogTitle.value = "新增短链接";
|
||||
showEditDialog.value = true;
|
||||
};
|
||||
const handleUpdate = async (id: number) => {
|
||||
reset();
|
||||
const { data } = await getDetailById(id);
|
||||
form.value = data.data;
|
||||
editDialogTitle.value = "修改短链接";
|
||||
showEditDialog.value = true;
|
||||
};
|
||||
const handleDelete = (id?: string) => {
|
||||
let ids: string;
|
||||
if (id) {
|
||||
ids = id;
|
||||
} else {
|
||||
ids = linkIds.value.join(",");
|
||||
}
|
||||
ElMessageBox.confirm("确认删除该短链接吗", "删除短链接", {
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
await deleteShortLink(ids);
|
||||
ElMessage.success("删除短链接成功");
|
||||
getList();
|
||||
});
|
||||
};
|
||||
|
||||
const submitForm = async () => {
|
||||
if (form.value.id) {
|
||||
await updateShortLink(form.value);
|
||||
ElMessage.success("短链接修改成功");
|
||||
showEditDialog.value = false;
|
||||
getList();
|
||||
} else {
|
||||
await addShortLink(form.value);
|
||||
ElMessage.success("短链接添加成功");
|
||||
showEditDialog.value = false;
|
||||
getList();
|
||||
}
|
||||
};
|
||||
const handleCurrentChange = (page: number) => {
|
||||
queryParams.value.pageNum = page;
|
||||
getList();
|
||||
};
|
||||
getList();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
#app-container {
|
||||
padding: 20px;
|
||||
|
||||
a.short-link {
|
||||
//overflow: hidden;
|
||||
//text-overflow: ellipsis;
|
||||
//white-space: nowrap;
|
||||
}
|
||||
|
||||
.jump-url {
|
||||
margin-top: 5px;
|
||||
font-size: 12px;
|
||||
//width: 80%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.visit-count {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 20px;
|
||||
|
||||
& > div {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
.short-link-table {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
margin-top: 20px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
</style>
|
11
tailwind.config.js
Normal file
11
tailwind.config.js
Normal file
@ -0,0 +1,11 @@
|
||||
/** @type {import("tailwindcss").Config} */
|
||||
module.exports = {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}"
|
||||
],
|
||||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
plugins: []
|
||||
};
|
8
tsconfig.config.json
Normal file
8
tsconfig.config.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.node.json",
|
||||
"include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "playwright.config.*"],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"types": ["node"]
|
||||
}
|
||||
}
|
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@vue/tsconfig/tsconfig.web.json",
|
||||
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.config.json"
|
||||
}
|
||||
]
|
||||
}
|
24
vite.config.ts
Normal file
24
vite.config.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
server: {
|
||||
proxy: {
|
||||
"/dev-api": {
|
||||
target: "http://192.168.110.10:1618",
|
||||
changeOrigin: true,
|
||||
rewrite: (p) => p.replace(/^\/dev-api/, "")
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url))
|
||||
}
|
||||
}
|
||||
|
||||
});
|
Reference in New Issue
Block a user