feat: init

This commit is contained in:
ex_zhangwenlei@exiot.cmcc
2024-01-02 21:50:01 +08:00
commit df02b23b2d
69 changed files with 7333 additions and 0 deletions

2
.env.development Normal file
View 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
View File

@@ -0,0 +1,2 @@
VITE_APP_TITLE="生产环境"
VITE_BASE_URL=https://24years.top

2
.env.test Normal file
View File

@@ -0,0 +1,2 @@
VITE_APP_TITLE="测试环境"
VITE_BASE_URL=http://localhost:4000

12
.eslintignore Normal file
View File

@@ -0,0 +1,12 @@
.md
node_modules
public
package.json
*.yaml
.gitignore
.eslintrc*
.babelrc
.eslintignore
.commitlintrc*
.env*
tsconfig*

57
.eslintrc.cjs Normal file
View 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
View 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
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
node .husky/scripts/verifyCommit.js

View 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
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

105
README.md Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,4 @@
// Generated by 'unplugin-auto-import'
export { };
declare global {
}

12
components.d.ts vendored Normal file
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

6
postcss.config.cjs Normal file
View File

@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
}
}

1
public/vite.svg Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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']
}
}

View 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>

View 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>

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -0,0 +1,10 @@
export const footerList = {
data: [
{
id: 0,
name: 'Github',
url: 'https://github.com/LOG1997',
icon: 'github',
},
],
};

View 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>

View File

@@ -0,0 +1,17 @@
export const navList = [
{
id: 0,
name: '首页',
url: 'home',
},
{
id: 1,
name: '项目',
url: 'project',
},
{
id: 2,
name: '关于',
url: 'about',
},
]

View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,7 @@
import { useUserStore } from './user';
export default function useStore() {
return {
user: useUserStore(),
};
}

34
src/store/user.ts Normal file
View 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
View 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 */
}

View 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,
);

View 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
View 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,
);

View 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
View File

@@ -0,0 +1 @@
import './global.scss';

View 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
View 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
View 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;
}
} ;

View 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,
);

View 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,
);

View 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,
);

View 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,
);

View 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
View 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
View File

@@ -0,0 +1,6 @@
// 用户的类型声明文件
export interface IUser {
name: string;
age: number;
}
// ...

3
src/utils/auth.ts Normal file
View File

@@ -0,0 +1,3 @@
export function getToken() {
return window.localStorage.getItem('userToken');
}

42
src/views/About/index.vue Normal file
View 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
View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
import { ref, reactive } from 'vue';
</script>
<template>Doc</template>
<style scoped></style>

View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

138
vite.config.ts Normal file
View 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$/],
},
},
};
});