feat: init
This commit is contained in:
2
.env.development
Normal file
2
.env.development
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_APP_TITLE="开发环境"
|
||||||
|
VITE_BASE_URL=http://127.0.0.1:4523/m1/1902803-0-default
|
||||||
2
.env.production
Normal file
2
.env.production
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_APP_TITLE="生产环境"
|
||||||
|
VITE_BASE_URL=https://24years.top
|
||||||
2
.env.test
Normal file
2
.env.test
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
VITE_APP_TITLE="测试环境"
|
||||||
|
VITE_BASE_URL=http://localhost:4000
|
||||||
12
.eslintignore
Normal file
12
.eslintignore
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.md
|
||||||
|
node_modules
|
||||||
|
public
|
||||||
|
package.json
|
||||||
|
*.yaml
|
||||||
|
.gitignore
|
||||||
|
.eslintrc*
|
||||||
|
.babelrc
|
||||||
|
.eslintignore
|
||||||
|
.commitlintrc*
|
||||||
|
.env*
|
||||||
|
tsconfig*
|
||||||
57
.eslintrc.cjs
Normal file
57
.eslintrc.cjs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
module.exports = {
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
extends: [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
],
|
||||||
|
overrides: [],
|
||||||
|
parser: "vue-eslint-parser",
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: "latest",
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
sourceType: "module",
|
||||||
|
},
|
||||||
|
plugins: ["vue", "@typescript-eslint"],
|
||||||
|
rules: {
|
||||||
|
"@typescript-eslint/ban-types": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
extendDefaults: true,
|
||||||
|
types: {
|
||||||
|
"{}": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// 关闭typescript类型为any的警告
|
||||||
|
"@typescript-eslint/no-explicit-any": ["off"],
|
||||||
|
// 驼峰命名,但忽略index
|
||||||
|
"vue/multi-word-component-names": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
ignores: ["index"], //需要忽略的组件名
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"no-console": "warn",
|
||||||
|
"no-debugger": "warn",
|
||||||
|
complexity: ["warn", { max: 5 }],
|
||||||
|
// 禁止使用多个空格
|
||||||
|
"no-multi-spaces": "error",
|
||||||
|
// 最大连续空行数
|
||||||
|
"no-multiple-empty-lines": ["error", { max: 2, maxEOF: 1, maxBOF: 0 }],
|
||||||
|
// 代码块中去除前后空行
|
||||||
|
"padded-blocks": ["error", "never"],
|
||||||
|
// 使用单引号,字符串中包含了一个其它引号 允许"a string containing 'single' quotes"
|
||||||
|
quotes: ["error", "single", { avoidEscape: true }],
|
||||||
|
// return之前必须空行
|
||||||
|
"newline-before-return": "error",
|
||||||
|
//文件末尾强制换行
|
||||||
|
"eol-last": ["error", "always"],
|
||||||
|
//禁止空格和 tab 的混合缩进
|
||||||
|
"no-mixed-spaces-and-tabs": ["error", "smart-tabs"],
|
||||||
|
},
|
||||||
|
};
|
||||||
130
.gitignore
vendored
Normal file
130
.gitignore
vendored
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
4
.husky/commit-msg
Normal file
4
.husky/commit-msg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
|
node .husky/scripts/verifyCommit.js
|
||||||
20
.husky/scripts/verifyCommit.js
Normal file
20
.husky/scripts/verifyCommit.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// const msg = require("fs").readFileSync(".git/COMMIT_EDITMSG", "utf-8").trim();
|
||||||
|
import fs from "fs";
|
||||||
|
const msg = fs.readFileSync(".git/COMMIT_EDITMSG", "utf-8").trim();
|
||||||
|
|
||||||
|
const commitRE =
|
||||||
|
/^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?: .{1,50}/;
|
||||||
|
const mergeRe = /^(Merge pull request|Merge branch)/;
|
||||||
|
if (!commitRE.test(msg)) {
|
||||||
|
if (!mergeRe.test(msg)) {
|
||||||
|
console.log("git commit信息校验不通过");
|
||||||
|
|
||||||
|
console.error(`git commit的信息格式不对, 需要使用 title(scope): desc的格式
|
||||||
|
比如 fix: xxbug
|
||||||
|
feat(test): add new
|
||||||
|
`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("git commit信息校验通过");
|
||||||
|
}
|
||||||
3
.vscode/extensions.json
vendored
Normal file
3
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar"]
|
||||||
|
}
|
||||||
105
README.md
Normal file
105
README.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Vue3 + Vite4 + Element + Windicss + Bootstrap
|
||||||
|
|
||||||
|
## 文件夹介绍
|
||||||
|
### 🅰️api
|
||||||
|
request.ts文件为axios封装,可在此拦截操作请求和回复
|
||||||
|
### 🆒components
|
||||||
|
组件文件夹,存放公共组件,如SvgIcon图标组件、Table表格组件等
|
||||||
|
### 👀hooks
|
||||||
|
封装hooks函数,公共函数的提取
|
||||||
|
### 🪪icons
|
||||||
|
存放图标组件,图标组件使用vite插件vite-plugin-svg-icon引入
|
||||||
|
```ts
|
||||||
|
plugins[
|
||||||
|
...
|
||||||
|
createSvgIconsPlugin({
|
||||||
|
// 指定需要缓存的图标文件夹
|
||||||
|
iconDirs: [path.resolve(process.cwd(), "src/icons")],
|
||||||
|
// 指定symbolId格式
|
||||||
|
symbolId: "icon-[dir]-[name]",
|
||||||
|
}),
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
使用时在组件内按如下方法使用即可
|
||||||
|
```ts
|
||||||
|
<svg-icon :name="'menu'" class="svgMenu cursor-pointer"></svg-icon>
|
||||||
|
```
|
||||||
|
### 🏬layout
|
||||||
|
整体的的布局组件,在router文件中根路径下引入。
|
||||||
|
包含Header、Main、Footer组件,布局使用了bootstrap的响应式布局
|
||||||
|
如菜单列表的写法
|
||||||
|
```ts
|
||||||
|
<nav class="navbar navbar-expand-lg">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<button
|
||||||
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbarSupportedContent"
|
||||||
|
aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
|
<!-- <span class="navbar-toggler-icon"></span> -->
|
||||||
|
<svg-icon :name="'open'"></svg-icon>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
class="nav-list collapse navbar-collapse"
|
||||||
|
id="navbarSupportedContent"
|
||||||
|
>
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
|
<li
|
||||||
|
class="nav-item"
|
||||||
|
v-for="item in navList.data"
|
||||||
|
:key="item.id"
|
||||||
|
@click="skip(item.url)"
|
||||||
|
>
|
||||||
|
<a class="nav-link active" aria-current="page" href="#">{{
|
||||||
|
item.name
|
||||||
|
}}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
```
|
||||||
|
使用媒体查询监听屏幕宽度自适应调整布局
|
||||||
|
如Header组件中,屏幕宽度大于1200px时始终保持headeer栏宽度为200px
|
||||||
|
```css
|
||||||
|
// @/layout/components/Header.vue
|
||||||
|
@media screen and (min-width: 1200px) {
|
||||||
|
.header-container {
|
||||||
|
width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
其中Main组件中写入
|
||||||
|
```ts
|
||||||
|
<router-view class="main-container-content"></router-view>
|
||||||
|
```
|
||||||
|
### 🏳️⚧️router
|
||||||
|
路由管理,使用history模式
|
||||||
|
### 🛒store
|
||||||
|
状态管理,使用pinia
|
||||||
|
### 🍟style
|
||||||
|
存放样式文件,模板里主要存放的是主题文件,使用scss。
|
||||||
|
文件中的函数以及样式在main.ts中引入过后即可使用
|
||||||
|
### 🥅types
|
||||||
|
定义的类型和接口
|
||||||
|
### 🎊views
|
||||||
|
界面组件
|
||||||
|
### 🙈App.vue
|
||||||
|
界面入口
|
||||||
|
### 🧵Main.ts
|
||||||
|
项目入口文件
|
||||||
|
### 🗽env文件
|
||||||
|
根据不同环境配置的路径地址,常量名称必须是`VITE_***`格式,在vite项目中引入时的方式为`import.meta.env.VITE_***`。
|
||||||
|
还需要在`vite.config.ts`文件中设置才可引用,具体见文件
|
||||||
|
### 🪔vite.config.ts
|
||||||
|
配置了icons的引入、elemnet的按需引入和自动注册、element图标的使用、windicss的引入、符号别名的设置、server的设置。
|
||||||
|
|
||||||
|
### 📦在.env文件中修改链接
|
||||||
41
__test__/Button.test.ts
Normal file
41
__test__/Button.test.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import Button from '@/components/Button/index.vue'
|
||||||
|
|
||||||
|
import { shallowMount } from '@vue/test-utils'
|
||||||
|
import { describe, expect, test } from 'vitest'
|
||||||
|
// 测试分组
|
||||||
|
describe('Button', () => {
|
||||||
|
// mount
|
||||||
|
test('Buttons slot text', () => {
|
||||||
|
// @vue/test-utils
|
||||||
|
const wrapper = shallowMount(Button, {
|
||||||
|
slots: {
|
||||||
|
default: 'Button',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
// 断言
|
||||||
|
expect(wrapper.text()).toBe('Button')
|
||||||
|
})
|
||||||
|
test('Button click', () => {
|
||||||
|
const wrapper = shallowMount(Button)
|
||||||
|
wrapper.trigger('click')
|
||||||
|
expect(wrapper.emitted('click')).toBeTruthy()
|
||||||
|
})
|
||||||
|
test('Button disabled', () => {
|
||||||
|
const wrapper = shallowMount(Button, {
|
||||||
|
props: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
wrapper.trigger('click')
|
||||||
|
expect(wrapper.emitted('click')).toBeFalsy()
|
||||||
|
})
|
||||||
|
test('Button not disabled', () => {
|
||||||
|
const wrapper = shallowMount(Button, {
|
||||||
|
props: {
|
||||||
|
disabled: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
wrapper.trigger('click')
|
||||||
|
expect(wrapper.emitted('click')).toBeTruthy()
|
||||||
|
})
|
||||||
|
})
|
||||||
31
__test__/Request.test.ts
Normal file
31
__test__/Request.test.ts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// test axios request
|
||||||
|
import Request from '@/api/request';
|
||||||
|
import { describe, it, expect, vi } from 'vitest';
|
||||||
|
import { mount, flushPromises } from '@vue/test-utils';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const fn = vi.fn();
|
||||||
|
const mockRes = {
|
||||||
|
data: {
|
||||||
|
code: 200,
|
||||||
|
success: true,
|
||||||
|
message: 'success',
|
||||||
|
data: {
|
||||||
|
name: 'test',
|
||||||
|
age: 18,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
fn(mockRes);
|
||||||
|
fn.mock.calls[0] === [mockRes];
|
||||||
|
|
||||||
|
describe('Request', () => {
|
||||||
|
it('should return data when request success', async () => {
|
||||||
|
const request = new Request();
|
||||||
|
const res = await request({
|
||||||
|
url: '/test',
|
||||||
|
method: 'GET',
|
||||||
|
});
|
||||||
|
expect(res).toEqual(mockRes.data);
|
||||||
|
});
|
||||||
|
});
|
||||||
4
auto-imports.d.ts
vendored
Normal file
4
auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
// Generated by 'unplugin-auto-import'
|
||||||
|
export { };
|
||||||
|
declare global {
|
||||||
|
}
|
||||||
12
components.d.ts
vendored
Normal file
12
components.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// generated by unplugin-vue-components
|
||||||
|
// We suggest you to commit this file into source control
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
import "@vue/runtime-core";
|
||||||
|
|
||||||
|
export { };
|
||||||
|
|
||||||
|
declare module "@vue/runtime-core" {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
16
index.html
Normal file
16
index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Vite + Vue + TS</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app" data-theme="default"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
62
package.json
Normal file
62
package.json
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"name": "log-lottery",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite --host 0.0.0.0",
|
||||||
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
|
"build:pre": "vue-tsc --noEmit && vite build --mode prebuild",
|
||||||
|
"test": "vitest",
|
||||||
|
"test:ui": "vitest --ui",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"lint": "eslint ./src --ext .vue,.js,.ts,.jsx,.tsx --fix"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
|
"@vueuse/core": "^10.6.1",
|
||||||
|
"axios": "^1.6.1",
|
||||||
|
"pinia": "^2.1.7",
|
||||||
|
"pinia-plugin-persist": "^1.0.0",
|
||||||
|
"svg-sprite-loader": "^6.0.11",
|
||||||
|
"vue": "^3.3.8",
|
||||||
|
"vue-router": "^4.2.5"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify-json/ep": "^1.1.12",
|
||||||
|
"@iconify-json/fluent": "^1.1.40",
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
|
"@testing-library/vue": "^8.0.0",
|
||||||
|
"@types/node": "^20.9.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||||
|
"@typescript-eslint/parser": "^6.11.0",
|
||||||
|
"@vitejs/plugin-vue": "^4.4.1",
|
||||||
|
"@vitest/ui": "^0.34.6",
|
||||||
|
"@vue/test-utils": "^2.4.2",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"daisyui": "^4.0.4",
|
||||||
|
"eslint": "^8.53.0",
|
||||||
|
"eslint-plugin-vue": "^9.18.1",
|
||||||
|
"fast-glob": "^3.3.2",
|
||||||
|
"happy-dom": "^12.10.3",
|
||||||
|
"husky": "^8.0.3",
|
||||||
|
"jsdom": "^22.1.0",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"postcss": "^8.4.31",
|
||||||
|
"rollup-plugin-visualizer": "^5.9.2",
|
||||||
|
"sass": "^1.69.5",
|
||||||
|
"sass-loader": "^13.3.2",
|
||||||
|
"tailwindcss": "^3.3.5",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"unplugin-auto-import": "^0.16.7",
|
||||||
|
"unplugin-icons": "^0.17.4",
|
||||||
|
"unplugin-vue-components": "^0.25.2",
|
||||||
|
"vite": "^4.5.0",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-inspect": "^0.7.42",
|
||||||
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
|
"vitest": "^0.34.6",
|
||||||
|
"vue-tsc": "^1.8.22"
|
||||||
|
}
|
||||||
|
}
|
||||||
5521
pnpm-lock.yaml
generated
Normal file
5521
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
postcss.config.cjs
Normal file
6
postcss.config.cjs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
9
src/App.vue
Normal file
9
src/App.vue
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<script setup lang="ts"></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<router-view></router-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import "@/style/global.module.scss";
|
||||||
|
</style>
|
||||||
15
src/api/main/index.ts
Normal file
15
src/api/main/index.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import request from '@/api/request';
|
||||||
|
export function getData(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/getData',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function postData(data: any) {
|
||||||
|
return request({
|
||||||
|
url: '/postData',
|
||||||
|
method: 'post',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
61
src/api/request.ts
Normal file
61
src/api/request.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
||||||
|
import type { InternalAxiosRequestConfig } from 'axios';
|
||||||
|
class Request {
|
||||||
|
private instance: AxiosInstance;
|
||||||
|
|
||||||
|
constructor(config: AxiosRequestConfig) {
|
||||||
|
this.instance = axios.create({
|
||||||
|
baseURL: '/api',
|
||||||
|
timeout: 10000,
|
||||||
|
...config,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加请求拦截器
|
||||||
|
this.instance.interceptors.request.use(
|
||||||
|
(config: InternalAxiosRequestConfig) => {
|
||||||
|
// 在发送请求之前做些什么
|
||||||
|
console.log('请求拦截器被触发');
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
console.error('请求拦截器发生错误:', error);
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 添加响应拦截器
|
||||||
|
this.instance.interceptors.response.use(
|
||||||
|
(response: AxiosResponse) => {
|
||||||
|
// 对响应数据做些什么
|
||||||
|
console.log('响应拦截器被触发');
|
||||||
|
const reponseData = response.data;
|
||||||
|
|
||||||
|
return reponseData;
|
||||||
|
},
|
||||||
|
(error: any) => {
|
||||||
|
// 对响应错误做些什么
|
||||||
|
console.error('响应拦截器发生错误:', error);
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async request<T>(config: AxiosRequestConfig): Promise<T> {
|
||||||
|
const response: AxiosResponse<T> = await this.instance.request(config);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 函数
|
||||||
|
function request<T>(config: AxiosRequestConfig): Promise<T> {
|
||||||
|
const instance = new Request(config);
|
||||||
|
|
||||||
|
return instance.request(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default request;
|
||||||
1
src/assets/vue.svg
Normal file
1
src/assets/vue.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 496 B |
9
src/auto-imports.d.ts
vendored
Normal file
9
src/auto-imports.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// noinspection JSUnusedGlobalSymbols
|
||||||
|
// Generated by unplugin-auto-import
|
||||||
|
export {}
|
||||||
|
declare global {
|
||||||
|
|
||||||
|
}
|
||||||
16
src/components.d.ts
vendored
Normal file
16
src/components.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/* prettier-ignore */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
export {}
|
||||||
|
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
HelloWorld: typeof import('./components/HelloWorld.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
SvgIcon: typeof import('./components/SvgIcon/index.vue')['default']
|
||||||
|
Table: typeof import('./components/Table/index.vue')['default']
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/components/HelloWorld.vue
Normal file
53
src/components/HelloWorld.vue
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
defineProps<{ msg: string }>();
|
||||||
|
|
||||||
|
const count = ref(0);
|
||||||
|
const addCount = () => {
|
||||||
|
count.value++;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h1 class="text-4xl font-bold py-6">{{ msg }}</h1>
|
||||||
|
|
||||||
|
<div class="card w-1200px">
|
||||||
|
<button
|
||||||
|
class="w-32 py-2 px-3 rounded-lg mx-auto my-3"
|
||||||
|
type="button"
|
||||||
|
@click="addCount"
|
||||||
|
>
|
||||||
|
count is {{ count }}
|
||||||
|
</button>
|
||||||
|
<p>
|
||||||
|
Edit
|
||||||
|
<code>components/HelloWorld.vue</code> to test HMR
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Check out
|
||||||
|
<a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
|
||||||
|
>create-vue</a
|
||||||
|
>, the official Vue + Vite starter
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Install
|
||||||
|
<a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
|
||||||
|
in your IDE for a better DX
|
||||||
|
</p>
|
||||||
|
<p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
// @import "@/style/global.scss";
|
||||||
|
.read-the-docs {
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: cornflowerblue;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
40
src/components/SvgIcon/index.vue
Normal file
40
src/components/SvgIcon/index.vue
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
const props = defineProps({
|
||||||
|
prefix: {
|
||||||
|
type: String,
|
||||||
|
default: 'icon',
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: '#242424',
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: String,
|
||||||
|
default: '24px',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const symbolId = computed(() => `#${props.prefix}-${props.name}`);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="svg-icon"
|
||||||
|
:width="props.size"
|
||||||
|
:height="props.size"
|
||||||
|
>
|
||||||
|
<use :xlink:href="symbolId" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.svg-icon {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
146
src/components/Table/index.vue
Normal file
146
src/components/Table/index.vue
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { PropType } from 'vue';
|
||||||
|
import { TableItemType } from '@/types/table';
|
||||||
|
const props = defineProps({
|
||||||
|
tableHeader: {
|
||||||
|
type: Array as PropType<TableItemType[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
tableData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
listLoading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
pagination: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
pageSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
currentPage: {
|
||||||
|
type: Number,
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
|
total: {
|
||||||
|
type: Number,
|
||||||
|
default: 100,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const emit = defineEmits(['handelSelect', 'handlePagination']);
|
||||||
|
// 选择
|
||||||
|
const handleSelectionChange = (val: []) => {
|
||||||
|
emit('handelSelect', val);
|
||||||
|
};
|
||||||
|
// 改变分页
|
||||||
|
const handleSizeChange = (val: number) => {
|
||||||
|
emit('handlePagination', { pageSize: val, currentPage: props.currentPage });
|
||||||
|
};
|
||||||
|
const handleCurrentChange = (val: number) => {
|
||||||
|
emit('handlePagination', {
|
||||||
|
pageSize: props.pageSize,
|
||||||
|
currentPage: val,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-table
|
||||||
|
:data="tableData"
|
||||||
|
:header-cell-style="{
|
||||||
|
background: '#f5f6fa',
|
||||||
|
color: '#555',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
fontSize: '12px',
|
||||||
|
}"
|
||||||
|
:cell-style="{ fontSize: '12px' }"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
>
|
||||||
|
<!-- 复选框 -->
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<!-- 数据 -->
|
||||||
|
<template v-for="(item, index) in tableHeader" :key="index">
|
||||||
|
<!-- 编辑按钮 -->
|
||||||
|
<el-table-column
|
||||||
|
v-if="item.actions"
|
||||||
|
:prop="item.prop"
|
||||||
|
:label="item.label"
|
||||||
|
:width="item.width"
|
||||||
|
fixed="right"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<div>
|
||||||
|
<el-button
|
||||||
|
text
|
||||||
|
size="small"
|
||||||
|
v-for="(action, index) in item.actions"
|
||||||
|
:key="index"
|
||||||
|
:type="action.type"
|
||||||
|
@click="action.func(scope.row)"
|
||||||
|
>{{ action.name }}</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- 图片展示 -->
|
||||||
|
<el-table-column
|
||||||
|
v-else-if="item.image"
|
||||||
|
:label="item.label"
|
||||||
|
:width="item.width"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div v-if="row[item.prop]">
|
||||||
|
<el-image
|
||||||
|
preview-teleported
|
||||||
|
:hide-on-click-modal="true"
|
||||||
|
:preview-src-list="[row[item.prop!]]"
|
||||||
|
:src="row[item.prop!]"
|
||||||
|
fit="cover"
|
||||||
|
style="width: 40px; height: 40px; border-radius: 8px"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-else>暂无图片</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!-- formatter信息展示-->
|
||||||
|
<el-table-column
|
||||||
|
v-else-if="item.formatter"
|
||||||
|
:label="item.label"
|
||||||
|
:width="item.width"
|
||||||
|
:align="item.aligen || 'center'"
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="item.getType!(row)"> {{ item.formatter(row) }}</el-tag>
|
||||||
|
</template></el-table-column
|
||||||
|
>
|
||||||
|
<!--普通信息展示 -->
|
||||||
|
<el-table-column
|
||||||
|
v-else
|
||||||
|
:label="item.label"
|
||||||
|
:prop="item.prop"
|
||||||
|
:width="item.width"
|
||||||
|
:align="item.aligen || 'center'"
|
||||||
|
></el-table-column>
|
||||||
|
</template>
|
||||||
|
</el-table>
|
||||||
|
<div v-if="pagination" class="float-right">
|
||||||
|
<el-pagination
|
||||||
|
background
|
||||||
|
:small="true"
|
||||||
|
layout="sizes,prev, pager, next"
|
||||||
|
:total="total"
|
||||||
|
:currentPage="currentPage"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:page-sizes="[10, 20, 30, 50]"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
5
src/components/index.ts
Normal file
5
src/components/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/**
|
||||||
|
*title: 自动导出组件
|
||||||
|
*/
|
||||||
|
export { default as Header } from './Header/index.vue';
|
||||||
|
export { default as Footer } from './Footer/index.vue';
|
||||||
19
src/hooks/useTheme.ts
Normal file
19
src/hooks/useTheme.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { useStorage } from '@vueuse/core';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
export const useTheme = (theme?: string) => {
|
||||||
|
const StorageTheme = useStorage('data-theme', theme) || ref('default');
|
||||||
|
const setTheme = (theme: string) => {
|
||||||
|
StorageTheme.value = theme;
|
||||||
|
const body = document.getElementsByTagName('body')[0];
|
||||||
|
if (body) {
|
||||||
|
body.setAttribute('data-theme', StorageTheme.value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (theme) {
|
||||||
|
setTheme(theme);
|
||||||
|
} else {
|
||||||
|
setTheme(StorageTheme.value as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { StorageTheme, setTheme };
|
||||||
|
};
|
||||||
1
src/icons/close.svg
Normal file
1
src/icons/close.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1664981520920" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1055" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M806.4 263.2l-45.6-45.6L512 467.2 263.2 217.6l-45.6 45.6L467.2 512 217.6 760.8l45.6 45.6L512 557.6l248.8 248.8 45.6-45.6L557.6 512z" p-id="1056"></path></svg>
|
||||||
|
After Width: | Height: | Size: 491 B |
1
src/icons/github.svg
Normal file
1
src/icons/github.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1666148264992" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1367" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M498.894518 100.608396c-211.824383 0-409.482115 189.041494-409.482115 422.192601 0 186.567139 127.312594 344.783581 295.065226 400.602887 21.13025 3.916193 32.039717-9.17701 32.039717-20.307512 0-10.101055 1.176802-43.343157 1.019213-78.596056-117.448946 25.564235-141.394311-49.835012-141.394311-49.835012-19.225877-48.805566-46.503127-61.793368-46.503127-61.793368-38.293141-26.233478 3.13848-25.611308 3.13848-25.611308 42.361807 2.933819 64.779376 43.443441 64.779376 43.443441 37.669948 64.574714 98.842169 45.865607 122.912377 35.094286 3.815909-27.262924 14.764262-45.918819 26.823925-56.431244-93.796246-10.665921-192.323237-46.90017-192.323237-208.673623 0-46.071292 16.498766-83.747379 43.449581-113.332185-4.379751-10.665921-18.805298-53.544497 4.076852-111.732757 0 0 35.46063-11.336186 116.16265 43.296085 33.653471-9.330506 69.783343-14.022365 105.654318-14.174837 35.869952 0.153496 72.046896 4.844332 105.753579 14.174837 80.606853-54.631248 116.00813-43.296085 116.00813-43.296085 22.935362 58.18826 8.559956 101.120049 4.180206 111.732757 27.052123 29.584806 43.443441 67.260893 43.443441 113.332185 0 162.137751-98.798167 197.850114-192.799074 208.262254 15.151072 13.088086 28.65155 38.804794 28.65155 78.17957 0 56.484456-0.459464 101.94381-0.459464 115.854635 0 11.235902 7.573489 24.381293 29.014824 20.2543C825.753867 867.330798 933.822165 709.10924 933.822165 522.700713c0-233.155201-224.12657-422.192601-434.927647-422.192601L498.894518 100.608396z" p-id="1368"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
1
src/icons/menu.svg
Normal file
1
src/icons/menu.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1664946129387" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="830" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M212 338c-24.852 0-45-20.148-45-45S187.148 248 212 248h600c24.852 0 45 20.148 45 45S836.852 338 812 338H212z m0 220c-24.852 0-45-20.148-45-45S187.148 468 212 468h600c24.852 0 45 20.148 45 45S836.852 558 812 558H212z m0 220c-24.852 0-45-20.148-45-45S187.148 688 212 688h600c24.852 0 45 20.148 45 45S836.852 778 812 778H212z" p-id="831"></path></svg>
|
||||||
|
After Width: | Height: | Size: 680 B |
1
src/icons/open.svg
Normal file
1
src/icons/open.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1673227601112" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2635" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M896 768c25.6 0 42.666667 17.066667 42.666667 42.666667s-17.066667 42.666667-42.666667 42.666666H128c-25.6 0-42.666667-17.066667-42.666667-42.666666s17.066667-42.666667 42.666667-42.666667h768z m0-298.666667c25.6 0 42.666667 17.066667 42.666667 42.666667s-17.066667 42.666667-42.666667 42.666667H128c-25.6 0-42.666667-17.066667-42.666667-42.666667s17.066667-42.666667 42.666667-42.666667h768z m0-298.666666c25.6 0 42.666667 17.066667 42.666667 42.666666s-17.066667 42.666667-42.666667 42.666667H128c-25.6 0-42.666667-17.066667-42.666667-42.666667s17.066667-42.666667 42.666667-42.666666h768z" p-id="2636"></path></svg>
|
||||||
|
After Width: | Height: | Size: 951 B |
10
src/layout/Footer/config.ts
Normal file
10
src/layout/Footer/config.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const footerList = {
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: 'Github',
|
||||||
|
url: 'https://github.com/LOG1997',
|
||||||
|
icon: 'github',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
25
src/layout/Footer/index.vue
Normal file
25
src/layout/Footer/index.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { footerList } from './config';
|
||||||
|
const skip = (url: string) => {
|
||||||
|
window.open(url);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="footer-container">
|
||||||
|
<ul class="flex justify-center">
|
||||||
|
<li
|
||||||
|
v-for="item in footerList.data"
|
||||||
|
:key="item.id"
|
||||||
|
@click="skip(item.url)"
|
||||||
|
class="flex items-center gap-1 cursor-pointer"
|
||||||
|
>
|
||||||
|
<svg-icon :name="item.icon"></svg-icon>
|
||||||
|
<p>{{ item.name }}</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
17
src/layout/Header/config.ts
Normal file
17
src/layout/Header/config.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export const navList = [
|
||||||
|
{
|
||||||
|
id: 0,
|
||||||
|
name: '首页',
|
||||||
|
url: 'home',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '项目',
|
||||||
|
url: 'project',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '关于',
|
||||||
|
url: 'about',
|
||||||
|
},
|
||||||
|
]
|
||||||
41
src/layout/Header/index.vue
Normal file
41
src/layout/Header/index.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { navList } from './config';
|
||||||
|
const skip = (url: string) => {
|
||||||
|
window.open(url, '_self');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="h-full header-container">
|
||||||
|
<div class="p-0 navbar bg-base-100">
|
||||||
|
<div class="navbar-start max-lg:w-full">
|
||||||
|
<div class="dropdown">
|
||||||
|
<label tabindex="0" class="btn btn-ghost lg:hidden">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M4 6h16M4 12h8m-8 6h16" />
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
<ul tabindex="0"
|
||||||
|
class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52 text-lg flex flex-col gap-2">
|
||||||
|
<li class="cursor-pointer hover:text-gray-100 hover:bg-base-200" v-for="item in navList" :key="item.id" @click="skip(item.url)">{{ item.name }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<a class="text-xl lg:pl-12 max-lg:mx-auto" href="https://vitejs.dev" target="_blank">
|
||||||
|
<img src="/vite.svg" class="logo" alt="Vite logo" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="hidden navbar-center lg:flex">
|
||||||
|
<ul class="flex gap-10 px-1 text-lg cursor-pointer menu menu-horizontal">
|
||||||
|
<li class="hover:text-gray-100" v-for="item in navList" :key="item.id" @click="skip(item.url)">{{ item.name }}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end max-lg:w-0">
|
||||||
|
<a class="btn">Button</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
21
src/layout/index.vue
Normal file
21
src/layout/index.vue
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import Header from './Header/index.vue';
|
||||||
|
import Footer from './Footer/index.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="w-screen">
|
||||||
|
<header class="shadow-2xl head-container h-14">
|
||||||
|
<Header></Header>
|
||||||
|
</header>
|
||||||
|
<main class="main-container w-screen box-content min-h-[calc(100vh-10rem)]">
|
||||||
|
<router-view class="main-container-content"></router-view>
|
||||||
|
</main>
|
||||||
|
<footer class="w-screen footer-container">
|
||||||
|
<Footer></Footer>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
19
src/main.ts
Normal file
19
src/main.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { createApp } from 'vue';
|
||||||
|
import './style.css';
|
||||||
|
|
||||||
|
import App from './App.vue';
|
||||||
|
|
||||||
|
const app = createApp(App);
|
||||||
|
// 全局svg组件
|
||||||
|
import 'virtual:svg-icons-register';
|
||||||
|
import svgIcon from '@/components/SvgIcon/index.vue';
|
||||||
|
// svg全局组件// 路由
|
||||||
|
import router from '@/router';
|
||||||
|
// pinia
|
||||||
|
import { createPinia } from 'pinia';
|
||||||
|
// pinia持久化
|
||||||
|
import piniaPluginPersist from 'pinia-plugin-persist';
|
||||||
|
const pinia = createPinia();
|
||||||
|
pinia.use(piniaPluginPersist);
|
||||||
|
app.component('svg-icon', svgIcon);
|
||||||
|
app.use(router).use(pinia).mount('#app');
|
||||||
39
src/router/index.ts
Normal file
39
src/router/index.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
import Layout from '@/layout/index.vue';
|
||||||
|
import Home from '@/views/Home/index.vue';
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/home',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
name: 'Home',
|
||||||
|
component: Home,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/guide',
|
||||||
|
name: 'Guide',
|
||||||
|
component: () => import('@/views/Guide/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/doc',
|
||||||
|
name: 'Doc',
|
||||||
|
component: () => import('@/views/Doc/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/about',
|
||||||
|
name: 'About',
|
||||||
|
component: () => import('@/views/About/index.vue'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
7
src/store/index.ts
Normal file
7
src/store/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { useUserStore } from './user';
|
||||||
|
|
||||||
|
export default function useStore() {
|
||||||
|
return {
|
||||||
|
user: useUserStore(),
|
||||||
|
};
|
||||||
|
}
|
||||||
34
src/store/user.ts
Normal file
34
src/store/user.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { IUser } from '@/types/user';
|
||||||
|
|
||||||
|
export const useUserStore = defineStore('user', {
|
||||||
|
state() {
|
||||||
|
return {
|
||||||
|
userList: [] as IUser[],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
getUserList(state) {
|
||||||
|
return state.userList;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
setUserList() {
|
||||||
|
const resList: IUser[] = [
|
||||||
|
{ name: '张三', age: 18 },
|
||||||
|
{ name: '李四', age: 19 },
|
||||||
|
];
|
||||||
|
this.userList = resList;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
persist: {
|
||||||
|
enabled: true,
|
||||||
|
strategies: [
|
||||||
|
{
|
||||||
|
// 如果要存储在localStorage中
|
||||||
|
// storage: localStorage,
|
||||||
|
paths: ['userList'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
55
src/style.css
Normal file
55
src/style.css
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
body,
|
||||||
|
html {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow-y: overlay;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 优化滚动条 */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
/*滚动条整体样式*/
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
background: rgba(12, 9, 9, 0.1);
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .tables::-webkit-scrollbar-track {
|
||||||
|
background: rgb(239, 239, 239);
|
||||||
|
border-radius: 2px;
|
||||||
|
} */
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
/* 滚动条 */
|
||||||
|
background: #909399;
|
||||||
|
-webkit-border-radius: 6px;
|
||||||
|
-moz-border-radius: 6px;
|
||||||
|
-ms-border-radius: 6px;
|
||||||
|
-o-border-radius: 6px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
-webkit-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
/* Webkit browsers */
|
||||||
|
-moz-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
/* Firefox */
|
||||||
|
-ms-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
/* IE9 */
|
||||||
|
-o-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
/* Opera(Old) */
|
||||||
|
box-shadow: inset 1px 1px 1px rgba(0, 0, 0, 0.25);
|
||||||
|
/* IE9+, News */
|
||||||
|
}
|
||||||
22
src/style/_blue_theme.scss
Normal file
22
src/style/_blue_theme.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #2e1f78;
|
||||||
|
$important-color: #02e5f6;
|
||||||
|
$font-color: #fbfbfb;
|
||||||
|
$light-color: #fd5913;
|
||||||
|
$normal-color: #15115d;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$blue_theme: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
22
src/style/_dark_theme.scss
Normal file
22
src/style/_dark_theme.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #1e1f25;
|
||||||
|
$important-color: #ff864d;
|
||||||
|
$font-color: #fbfbfb;
|
||||||
|
$light-color: #00e6f6;
|
||||||
|
$normal-color: #183561;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$dark_theme: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
22
src/style/_default.scss
Normal file
22
src/style/_default.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #fbfbfb;
|
||||||
|
$important-color: #6a47bb;
|
||||||
|
$font-color: #030303;
|
||||||
|
$light-color: #409eff;
|
||||||
|
$normal-color: #fbfbfb;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$default: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
22
src/style/_purple_theme.scss
Normal file
22
src/style/_purple_theme.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #0a142e;
|
||||||
|
$important-color: #8c5ff6;
|
||||||
|
$font-color: #fbfbfb;
|
||||||
|
$light-color: #c678fd;
|
||||||
|
$normal-color: #0a142e;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$purple_theme: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
1
src/style/changeTheme.ts
Normal file
1
src/style/changeTheme.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
import './global.scss';
|
||||||
14
src/style/global.module.scss
Normal file
14
src/style/global.module.scss
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
:root {
|
||||||
|
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-synthesis: none;
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
// background: $theme-bg-color;
|
||||||
|
}
|
||||||
11
src/style/global.scss
Normal file
11
src/style/global.scss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// global.scss
|
||||||
|
@import "./theme/index.scss";
|
||||||
|
// variables.scss
|
||||||
|
$primary-color: #fe4e5e;
|
||||||
|
$background-color: #fefefe;
|
||||||
|
$padding: 124px;
|
||||||
|
// :export {
|
||||||
|
// primaryColor: $primary-color;
|
||||||
|
// backgroundColor: $background-color;
|
||||||
|
// padding: $padding;
|
||||||
|
// }
|
||||||
50
src/style/index.scss
Normal file
50
src/style/index.scss
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
@import "./default.scss"; //默认主题颜色
|
||||||
|
@import "./dark_theme.scss"; // 红色主题颜色
|
||||||
|
@import "./blue_theme.scss"; // 灰色主题颜色
|
||||||
|
@import "./purple_theme.scss"; // 灰色主题颜色
|
||||||
|
//生成对应元素的主题样式代码
|
||||||
|
//主题参数
|
||||||
|
$themes: (
|
||||||
|
default: $default,
|
||||||
|
blue_theme: $blue_theme,
|
||||||
|
dark_theme: $dark_theme,
|
||||||
|
purple_theme: $purple_theme,
|
||||||
|
);
|
||||||
|
// 生成body的主题样式
|
||||||
|
@mixin themeify {
|
||||||
|
@each $themes-key, $themes-map in $themes {
|
||||||
|
$themes-map: $themes-map !global;
|
||||||
|
&[data-theme="#{$themes-key}"] {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 生成子元素样式
|
||||||
|
@mixin themeChildify {
|
||||||
|
@each $themes-key, $themes-map in $themes {
|
||||||
|
$themes-map: $themes-map !global;
|
||||||
|
[data-theme="#{$themes-key}"] & {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取主题对应的theme-color
|
||||||
|
@function getThemeColor($key) {
|
||||||
|
$currentTheme: map-get($themes, $key);
|
||||||
|
@return map-get($currentTheme, "theme-bg-color");
|
||||||
|
}
|
||||||
|
@function themed($key) {
|
||||||
|
@if map-get($themes-map, $key) {
|
||||||
|
@return map-get($themes-map, $key);
|
||||||
|
} @else {
|
||||||
|
@return $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 随机数颜色
|
||||||
|
@function themedRandom($key) {
|
||||||
|
@if map-get($themes-map, $key) {
|
||||||
|
@return map-get($themes-map, $key);
|
||||||
|
} @else {
|
||||||
|
@return $key;
|
||||||
|
}
|
||||||
|
} ;
|
||||||
22
src/style/theme/_blue_theme.scss
Normal file
22
src/style/theme/_blue_theme.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #2e1f78;
|
||||||
|
$important-color: #02e5f6;
|
||||||
|
$font-color: #fbfbfb;
|
||||||
|
$light-color: #fd5913;
|
||||||
|
$normal-color: #15115d;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$blue_theme: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
22
src/style/theme/_dark_theme.scss
Normal file
22
src/style/theme/_dark_theme.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #1e1f25;
|
||||||
|
$important-color: #ff864d;
|
||||||
|
$font-color: #fbfbfb;
|
||||||
|
$light-color: #00e6f6;
|
||||||
|
$normal-color: #183561;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$dark_theme: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
22
src/style/theme/_default.scss
Normal file
22
src/style/theme/_default.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #fbfbfb;
|
||||||
|
$important-color: #6a47bb;
|
||||||
|
$font-color: #030303;
|
||||||
|
$light-color: #409eff;
|
||||||
|
$normal-color: #fbfbfb;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$default: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
22
src/style/theme/_purple_theme.scss
Normal file
22
src/style/theme/_purple_theme.scss
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
$theme-color: #0a142e;
|
||||||
|
$important-color: #8c5ff6;
|
||||||
|
$font-color: #fbfbfb;
|
||||||
|
$light-color: #c678fd;
|
||||||
|
$normal-color: #0a142e;
|
||||||
|
$dark-card-color: #fbfbfb;
|
||||||
|
|
||||||
|
$purple_theme: (
|
||||||
|
theme-bg-color: $theme-color,
|
||||||
|
font-color: $font-color,
|
||||||
|
font-color-second: darken($font-color, 20%),
|
||||||
|
light-bg-color: $light-color,
|
||||||
|
normal-color: $normal-color,
|
||||||
|
header-bg-color: lighten($theme-color, 10%),
|
||||||
|
header-bg-color-hover: lighten($theme-color, 20%),
|
||||||
|
shawdow-color: darken($theme-color, 5%),
|
||||||
|
shawdow-color-hover: darken($theme-color, 15%),
|
||||||
|
border-color: lighten($theme-color, 20%),
|
||||||
|
important-color: lighten($important-color, 20%),
|
||||||
|
important-bg-color: transparentize($important-color, 0.9),
|
||||||
|
dark-card-color: $dark-card-color,
|
||||||
|
);
|
||||||
50
src/style/theme/index.scss
Normal file
50
src/style/theme/index.scss
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
@import "./default.scss"; //默认主题颜色
|
||||||
|
@import "./dark_theme.scss"; // 红色主题颜色
|
||||||
|
@import "./blue_theme.scss"; // 灰色主题颜色
|
||||||
|
@import "./purple_theme.scss"; // 灰色主题颜色
|
||||||
|
//生成对应元素的主题样式代码
|
||||||
|
//主题参数
|
||||||
|
$themes: (
|
||||||
|
default: $default,
|
||||||
|
blue_theme: $blue_theme,
|
||||||
|
dark_theme: $dark_theme,
|
||||||
|
purple_theme: $purple_theme,
|
||||||
|
);
|
||||||
|
// 生成body的主题样式
|
||||||
|
@mixin themeify {
|
||||||
|
@each $themes-key, $themes-map in $themes {
|
||||||
|
$themes-map: $themes-map !global;
|
||||||
|
&[data-theme="#{$themes-key}"] {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 生成子元素样式
|
||||||
|
@mixin themeChildify {
|
||||||
|
@each $themes-key, $themes-map in $themes {
|
||||||
|
$themes-map: $themes-map !global;
|
||||||
|
[data-theme="#{$themes-key}"] & {
|
||||||
|
@content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取主题对应的theme-color
|
||||||
|
@function getThemeColor($key) {
|
||||||
|
$currentTheme: map-get($themes, $key);
|
||||||
|
@return map-get($currentTheme, "theme-bg-color");
|
||||||
|
}
|
||||||
|
@function themed($key) {
|
||||||
|
@if map-get($themes-map, $key) {
|
||||||
|
@return map-get($themes-map, $key);
|
||||||
|
} @else {
|
||||||
|
@return $key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 随机数颜色
|
||||||
|
@function themedRandom($key) {
|
||||||
|
@if map-get($themes-map, $key) {
|
||||||
|
@return map-get($themes-map, $key);
|
||||||
|
} @else {
|
||||||
|
@return $key;
|
||||||
|
}
|
||||||
|
} ;
|
||||||
18
src/types/table.ts
Normal file
18
src/types/table.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export interface TableItemType {
|
||||||
|
prop: string;
|
||||||
|
label: string;
|
||||||
|
width: number;
|
||||||
|
image?: boolean;
|
||||||
|
actions?: actionsItemType[];
|
||||||
|
aligen?: string;
|
||||||
|
formatter?: ItemFuncType;
|
||||||
|
getType?: ItemFuncType;
|
||||||
|
}
|
||||||
|
interface ItemFuncType {
|
||||||
|
(row: any): any;
|
||||||
|
}
|
||||||
|
interface actionsItemType {
|
||||||
|
name: string;
|
||||||
|
func: any;
|
||||||
|
type?: any;
|
||||||
|
}
|
||||||
6
src/types/user.ts
Normal file
6
src/types/user.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// 用户的类型声明文件
|
||||||
|
export interface IUser {
|
||||||
|
name: string;
|
||||||
|
age: number;
|
||||||
|
}
|
||||||
|
// ...
|
||||||
3
src/utils/auth.ts
Normal file
3
src/utils/auth.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export function getToken() {
|
||||||
|
return window.localStorage.getItem('userToken');
|
||||||
|
}
|
||||||
42
src/views/About/index.vue
Normal file
42
src/views/About/index.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const skip = (url: string) => {
|
||||||
|
router.push({
|
||||||
|
path: url,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const formInline = reactive({
|
||||||
|
user: '',
|
||||||
|
region: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
console.log('submit!');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<h2>About</h2>
|
||||||
|
<button @click="skip('/home')">to Home</button>
|
||||||
|
<el-form :inline="true" :model="formInline" class="demo-form-inline">
|
||||||
|
<el-form-item label="Approved by">
|
||||||
|
<el-input v-model="formInline.user" placeholder="Approved by" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Activity zone">
|
||||||
|
<el-select v-model="formInline.region" placeholder="Activity zone">
|
||||||
|
<el-option label="Zone one" value="shanghai" />
|
||||||
|
<el-option label="Zone two" value="beijing" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="onSubmit">Query</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
7
src/views/Doc/index.vue
Normal file
7
src/views/Doc/index.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>Doc</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
7
src/views/Guide/index.vue
Normal file
7
src/views/Guide/index.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>project</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
71
src/views/Home/index.vue
Normal file
71
src/views/Home/index.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import HelloWorld from '../../components/HelloWorld.vue';
|
||||||
|
import useStore from '@/store/index';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { IUser } from '@/types/user';
|
||||||
|
|
||||||
|
import { getData } from '@/api/main';
|
||||||
|
const store = useStore();
|
||||||
|
const router = useRouter();
|
||||||
|
const data = ref<any>(null);
|
||||||
|
const fetchData = async () => {
|
||||||
|
const res = await getData({});
|
||||||
|
data.value = res;
|
||||||
|
console.log('😊data.value:', data.value);
|
||||||
|
};
|
||||||
|
store.user.setUserList();
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
let user = ref<IUser[]>([]);
|
||||||
|
const getUser = () => {
|
||||||
|
user.value = store.user.getUserList;
|
||||||
|
console.log('😊user.value:', user.value);
|
||||||
|
};
|
||||||
|
const skip = (url: string) => {
|
||||||
|
router.push({
|
||||||
|
path: url,
|
||||||
|
query: { id: 1 },
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<!-- <svg-icon :name="'menu'" class="cursor-pointer svgMenu"></svg-icon> -->
|
||||||
|
<a href="https://vitejs.dev" target="_blank">
|
||||||
|
<img src="/vite.svg" class="logo" alt="Vite logo" />
|
||||||
|
</a>
|
||||||
|
<a href="https://vuejs.org/" target="_blank">
|
||||||
|
<img src="../../assets/vue.svg" class="logo vue" alt="Vue logo" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-center gap-6">
|
||||||
|
<button class="btn btn-primary" @click="getUser">pinia test</button>
|
||||||
|
<button class="btn btn-outline btn-secondary" @click="skip('about')"> router test</button>
|
||||||
|
<button class="btn glass" @click="fetchData">fetch</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<HelloWorld
|
||||||
|
class="flex flex-col items-center mx-auto text-center flex-column"
|
||||||
|
msg="Vite + Vue"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
height: 10em;
|
||||||
|
padding: 1.5em;
|
||||||
|
will-change: filter;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.logo:hover {
|
||||||
|
filter: drop-shadow(0 0 2em #646cffaa);
|
||||||
|
}
|
||||||
|
.logo.vue:hover {
|
||||||
|
filter: drop-shadow(0 0 2em #42b883aa);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
7
src/vite-env.d.ts
vendored
Normal file
7
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
|
||||||
|
declare module '*.vue' {
|
||||||
|
import type { DefineComponent } from 'vue';
|
||||||
|
const component: DefineComponent<{}, {}, any>;
|
||||||
|
export default component;
|
||||||
|
}
|
||||||
22
tailwind.config.js
Normal file
22
tailwind.config.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
darkMode: 'class',
|
||||||
|
corePlugins: {
|
||||||
|
preflight: false
|
||||||
|
},
|
||||||
|
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||||
|
plugins: [require('@tailwindcss/typography'),require('daisyui')],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
colors: {
|
||||||
|
bg_color: 'var(--el-bg-color)',
|
||||||
|
primary: 'var(--el-color-primary)',
|
||||||
|
primary_light_9: 'var(--el-color-primary-light-9)',
|
||||||
|
text_color_primary: 'var(--el-text-color-primary)',
|
||||||
|
text_color_regular: 'var(--el-text-color-regular)',
|
||||||
|
text_color_disabled: 'var(--el-text-color-disabled)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"strict": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
// 单元测试的支持
|
||||||
|
"types": ["vitest/globals"],
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["src/*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
||||||
9
tsconfig.node.json
Normal file
9
tsconfig.node.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
138
vite.config.ts
Normal file
138
vite.config.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
/// <reference types="vitest" />
|
||||||
|
|
||||||
|
import { defineConfig, loadEnv } from 'vite';
|
||||||
|
import vue from '@vitejs/plugin-vue';
|
||||||
|
import path from 'path';
|
||||||
|
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite';
|
||||||
|
import Components from 'unplugin-vue-components/vite';
|
||||||
|
import Icons from 'unplugin-icons/vite';
|
||||||
|
import IconsResolver from 'unplugin-icons/resolver';
|
||||||
|
import { visualizer } from 'rollup-plugin-visualizer';
|
||||||
|
import viteCompression from 'vite-plugin-compression';
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(({ mode }) => {
|
||||||
|
const env = loadEnv(mode, __dirname);
|
||||||
|
const chunkName = mode == 'prebuild' ? '[name]' : 'chunk';
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
viteCompression({
|
||||||
|
verbose: true,
|
||||||
|
disable: false,
|
||||||
|
threshold: 10240,
|
||||||
|
algorithm: 'gzip',
|
||||||
|
ext: '.gz',
|
||||||
|
}),
|
||||||
|
visualizer({
|
||||||
|
emitFile: true, //是否被触摸
|
||||||
|
filename: 'test.html', //生成分析网页文件名
|
||||||
|
open: true, //在默认用户代理中打开生成的文件
|
||||||
|
gzipSize: true, //从源代码中收集 gzip 大小并将其显示在图表中
|
||||||
|
brotliSize: true, //从源代码中收集 brotli 大小并将其显示在图表中
|
||||||
|
}),
|
||||||
|
|
||||||
|
createSvgIconsPlugin({
|
||||||
|
// 指定需要缓存的图标文件夹
|
||||||
|
iconDirs: [path.resolve(process.cwd(), 'src/icons')],
|
||||||
|
// 指定symbolId格式
|
||||||
|
symbolId: 'icon-[dir]-[name]',
|
||||||
|
}),
|
||||||
|
AutoImport({
|
||||||
|
resolvers: [
|
||||||
|
// 自动导入图标组件
|
||||||
|
IconsResolver({
|
||||||
|
prefix: 'Icon',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
dts: path.resolve(path.resolve(__dirname, 'src'), 'auto-imports.d.ts'),
|
||||||
|
}),
|
||||||
|
Components({
|
||||||
|
resolvers: [
|
||||||
|
// 自动注册图标组件
|
||||||
|
IconsResolver({
|
||||||
|
enabledCollections: ['ep'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
dts: path.resolve(path.resolve(__dirname, 'src'), 'components.d.ts'),
|
||||||
|
}),
|
||||||
|
Icons({
|
||||||
|
autoInstall: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
css: {
|
||||||
|
preprocessorOptions: {
|
||||||
|
scss: {
|
||||||
|
additionalData: '@import "@/style/global.scss";',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// postcss: {
|
||||||
|
// plugins: [
|
||||||
|
// require('tailwindcss'),
|
||||||
|
// require('autoprefixer'),
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: 6719,
|
||||||
|
proxy: {
|
||||||
|
'/api': {
|
||||||
|
target: env.VITE_BASE_URL,
|
||||||
|
// 是否跨域
|
||||||
|
changeOrigin: true,
|
||||||
|
// 路径重写
|
||||||
|
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': path.resolve(__dirname, './src'),
|
||||||
|
'~bootstrap': path.resolve(__dirname, 'node_modules/bootstrap'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
minify: 'terser',
|
||||||
|
terserOptions: {
|
||||||
|
compress: {
|
||||||
|
//生产环境时移除console
|
||||||
|
drop_console: true,
|
||||||
|
drop_debugger: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 关闭文件计算
|
||||||
|
reportCompressedSize: false,
|
||||||
|
// 关闭生成map文件 可以达到缩小打包体积
|
||||||
|
sourcemap: false, // 这个生产环境一定要关闭,不然打包的产物会很大
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
chunkFileNames: `js/${chunkName}-[hash].js`, // 引入文件名的名称
|
||||||
|
entryFileNames: `js/${chunkName}-[hash].js`, // 包的入口文件名称
|
||||||
|
assetFileNames: `[ext]/${chunkName}-[hash].[ext]`, // 资源文件像 字体,图片等
|
||||||
|
manualChunks(id: any): string {
|
||||||
|
if (id.includes('node_modules')) {
|
||||||
|
return id
|
||||||
|
.toString()
|
||||||
|
.split('node_modules/')[1]
|
||||||
|
.split('/')[0]
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 使用这个必须在上面加/// <reference types="vitest" /> 不然会有类型报错
|
||||||
|
test: {
|
||||||
|
globals: true, // --> 0.8.1+ 请修改成globals
|
||||||
|
environment: 'jsdom',
|
||||||
|
// include: ['**/__tests__/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||||
|
// passWithNoTests: true,
|
||||||
|
transformMode: {
|
||||||
|
web: [/\.[jt]sx$/],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user