Compare commits
3 Commits
fe5db6271c
...
757369ebc6
| Author | SHA1 | Date |
|---|---|---|
|
|
757369ebc6 | |
|
|
3306cab187 | |
|
|
c4fcc28ed2 |
82
README.md
82
README.md
|
|
@ -1,81 +1,3 @@
|
||||||
# Vue 3 + TypeScript + Vite 6.x + VueRouter 4.x + Pinia 模板
|
# 支付统计项目
|
||||||
|
|
||||||
pnpm作为包管理,主要可以解决幽灵依赖包的问题
|
深泉支付统计项目
|
||||||
|
|
||||||
1. Pinia 状态管理库带加密功能
|
|
||||||
本地建立.env
|
|
||||||
文件中`VITE_SECRET_KEY=xxx`
|
|
||||||
|
|
||||||
2. 自定义Axios 加密参数 错误日志记录
|
|
||||||
|
|
||||||
3. VueRouter 4.x 路由 权限控制 动态缓存
|
|
||||||
import { removeKeepAliveCache, resetKeepAliveCache } from '@/router/keepAlive'
|
|
||||||
const instance = getCurrentInstance()!
|
|
||||||
onBeforeRouteLeave((to) => {
|
|
||||||
if (to.path === '/C') {
|
|
||||||
removeKeepAliveCache(instance)
|
|
||||||
} else {
|
|
||||||
resetKeepAliveCache(instance)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
通过 removeKeepAliveCache, resetKeepAliveCache 来维护exclude数组里面的内容达到 a 进入C后返回到A页面时,a页面的缓存被清楚,进入其他页面返回则不被清除缓存
|
|
||||||
|
|
||||||
权限控制
|
|
||||||
有身份权限的写在privateRoutes.ts中
|
|
||||||
没有身份权限的写在publicRoutes.ts中
|
|
||||||
|
|
||||||
4. 项目版本检测,确保启动顺利。检测node版本和工具包的版本是否符合要求。
|
|
||||||
可以根据项目需求修改package.json中的engines字段。
|
|
||||||
``` shell
|
|
||||||
pnpm run check-env
|
|
||||||
```
|
|
||||||
5. prettier 统一的代码格式,这样保存格式化的时候就不会因为不同的格式化工具导致其他的内容也出现修改。vscode配合prettier插件
|
|
||||||
.prettierrc.json的内容就是指定代码的格式的配置文件
|
|
||||||
|
|
||||||
6. SvgIcon组件,更加优雅的使用svg图片
|
|
||||||
将图片放入到 /public/icons 下面,name就是图片的名字例如certificate.svg
|
|
||||||
``` html
|
|
||||||
<SvgIcon name="certificate" class="w-10 h-10"></SvgIcon>
|
|
||||||
|
|
||||||
```
|
|
||||||
推荐使用较少的svg代码图片,不然html的体积会进一步增大
|
|
||||||
|
|
||||||
如果是大的背景图推荐使用`vite`的动态导入
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
|
|
||||||
const getPath = async () => {
|
|
||||||
const { default: svg } = await import("/src/assets/svg-img/certificate.svg?raw");
|
|
||||||
test.value = svg;
|
|
||||||
};
|
|
||||||
|
|
||||||
getPath();
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
7. unocss 即时原子 CSS 引擎。主要想解决遍地css文件的麻烦。预设了tailwind css的预算值。所以可以直接对着tailwind css的官方文档编写
|
|
||||||
|
|
||||||
8. composables 这个文件夹推荐将业务逻辑写在此处的x.ts文件内,然后搭配 vue3的组合式语法。解决视图层和业务层的代码分离。
|
|
||||||
|
|
||||||
9. 异步加载动态组件, 针对大的页面要加载很多的JS,可以异步错开或者等待条件允许再加载。看个人业务需求
|
|
||||||
```html
|
|
||||||
|
|
||||||
<component :is="currentComponent"></component>
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
|
|
||||||
let currentComponent = ref(defineAsyncComponent(() => import('@/components/TestComponent.vue')))
|
|
||||||
let flag = false
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
if (flag) {
|
|
||||||
currentComponent.value = defineAsyncComponent(() => import('@/components/TestComponent2.vue'));
|
|
||||||
} else {
|
|
||||||
currentComponent.value = defineAsyncComponent(() => import('@/components/TestComponent.vue'));
|
|
||||||
}
|
|
||||||
flag = !flag
|
|
||||||
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
@ -6,7 +6,6 @@ import { useUserStore } from "@/store/user";
|
||||||
* @LastEditors: error: git config user.name & please set dead value or install git
|
* @LastEditors: error: git config user.name & please set dead value or install git
|
||||||
* @LastEditTime: 2024-09-01
|
* @LastEditTime: 2024-09-01
|
||||||
* @Description: 请求封装
|
* @Description: 请求封装
|
||||||
* @FilePath: /free-music-react/src/lib/customFetch.ts
|
|
||||||
*/
|
*/
|
||||||
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
|
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
|
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
|
||||||
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
|
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
|
||||||
<div class="text-[25px]">690</div>
|
<div class="text-[25px]">{{ offlineTotal }}</div>
|
||||||
<div class="text-[20px]">人</div>
|
<div class="text-[20px]">人</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线下</div>
|
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线下</div>
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
};
|
};
|
||||||
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
|
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
|
||||||
|
|
||||||
const askSectionData = inject("askSectionData", ref<{ offline: any[] }>({ offline: [] }));
|
const askSectionData = inject("askSectionData", ref<{ offline: any[],scource:any[] }>({ offline: [],scource:[] }));
|
||||||
|
|
||||||
const chartData = ref<any[]>([]);
|
const chartData = ref<any[]>([]);
|
||||||
|
|
||||||
|
|
@ -81,6 +81,8 @@
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const offlineTotal = ref<any>({})
|
||||||
|
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
if (askSectionData.value.offline && askSectionData.value.offline.length > 0) {
|
if (askSectionData.value.offline && askSectionData.value.offline.length > 0) {
|
||||||
chartData.value = askSectionData.value.offline.map((item, index) => ({
|
chartData.value = askSectionData.value.offline.map((item, index) => ({
|
||||||
|
|
@ -88,6 +90,7 @@
|
||||||
value: item.total,
|
value: item.total,
|
||||||
color: colorList.value[index % colorList.value.length],
|
color: colorList.value[index % colorList.value.length],
|
||||||
}));
|
}));
|
||||||
|
offlineTotal.value = askSectionData.value.scource.filter(item => item.tag==="线下")[0].total
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
|
<div class="leading-[1] absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] z-3 flex items-center flex-col font-700 italic">
|
||||||
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
|
<div class="bg-gradient-to-b from-[#8FC8FF] to-white bg-clip-text text-transparent flex items-baseline">
|
||||||
<div class="text-[25px]">690</div>
|
<div class="text-[25px]">{{ onlineTotal }}</div>
|
||||||
<div class="text-[20px]">人</div>
|
<div class="text-[20px]">人</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线上</div>
|
<div class="text-[#FFFFFF] text-[16px] text-shadow-[0px_2px_2px_rgba(12,32,72,0.42)] mt-[7px]">线上</div>
|
||||||
|
|
@ -71,13 +71,17 @@
|
||||||
|
|
||||||
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
|
const colorList = ref(["#0783FA", "#07D1FA", "#20E6A4", "#FFD15C"]);
|
||||||
|
|
||||||
const askSectionData = inject("askSectionData", ref<{ online: any[] }>({ online: [] }));
|
const askSectionData = inject("askSectionData", ref<{ online: any[]; scource: any[] }>({ online: [], scource: [] }));
|
||||||
|
|
||||||
const chartData = ref<any[]>([]);
|
const chartData = ref<any[]>([]);
|
||||||
|
const onlineTotal = ref<any>({});
|
||||||
|
|
||||||
watch(() => askSectionData.value, () => {
|
watch(
|
||||||
initData()
|
() => askSectionData.value,
|
||||||
});
|
() => {
|
||||||
|
initData();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const initData = () => {
|
const initData = () => {
|
||||||
if (askSectionData.value.online && askSectionData.value.online.length > 0) {
|
if (askSectionData.value.online && askSectionData.value.online.length > 0) {
|
||||||
|
|
@ -86,8 +90,9 @@
|
||||||
value: item.total,
|
value: item.total,
|
||||||
color: colorList.value[index % colorList.value.length],
|
color: colorList.value[index % colorList.value.length],
|
||||||
}));
|
}));
|
||||||
|
onlineTotal.value = askSectionData.value.scource.filter((item) => item.tag === "线上")[0].total;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
getHeaderLeftSvg();
|
getHeaderLeftSvg();
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import * as echarts from "echarts";
|
import * as echarts from "echarts";
|
||||||
import { ref, onMounted, onUnmounted, defineProps, watch } from "vue";
|
|
||||||
|
|
||||||
interface DataItem {
|
interface DataItem {
|
||||||
tag: string;
|
tag: string;
|
||||||
|
|
|
||||||
|
|
@ -83,13 +83,16 @@
|
||||||
{ headers: { "content-type": "application/json; charset=utf-8" } },
|
{ headers: { "content-type": "application/json; charset=utf-8" } },
|
||||||
).then((resp) => {
|
).then((resp) => {
|
||||||
if (resp.code === 200) {
|
if (resp.code === 200) {
|
||||||
|
if(resp.result == 401){
|
||||||
|
alert("账号或密码错误");
|
||||||
|
}else{
|
||||||
// 登录成功,存储token
|
// 登录成功,存储token
|
||||||
const { accessToken, refreshToken } = resp.result as { refreshToken: string; accessToken: string };
|
const { accessToken, refreshToken } = resp.result as { refreshToken: string; accessToken: string };
|
||||||
userStore.setAccessToken(accessToken);
|
userStore.setAccessToken(accessToken);
|
||||||
userStore.setRefreshToken(refreshToken);
|
userStore.setRefreshToken(refreshToken);
|
||||||
|
|
||||||
// 跳转到首页
|
// 跳转到首页
|
||||||
router.push("/");
|
router.push("/");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 登录失败提示
|
// 登录失败提示
|
||||||
alert(resp.message || "登录失败");
|
alert(resp.message || "登录失败");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue