feat: implement gas and delivery admin systems

This commit is contained in:
david
2026-07-30 14:16:58 +08:00
parent 1093385f95
commit f5ecc0d973
252 changed files with 49385 additions and 363 deletions

View File

@@ -0,0 +1,25 @@
import type { EChartsOption } from 'echarts';
import { computed } from 'vue';
import { useAppStore } from '@/store';
// for code hints
// import { SeriesOption } from 'echarts';
// Because there are so many configuration items, this provides a relatively convenient code hint.
// When using vue, pay attention to the reactive issues. It is necessary to ensure that corresponding functions can be triggered, TypeScript does not report errors, and code writing is convenient.
type optionsFn = (isDark: boolean) => EChartsOption;
export default function useChartOption(sourceOption: optionsFn) {
const appStore = useAppStore();
const isDark = computed(() => {
return appStore.theme === 'dark';
});
// echarts support https://echarts.apache.org/zh/theme-builder.html
// It's not used here
// TODO echarts themes
const chartOption = computed<EChartsOption>(() => {
return sourceOption(isDark.value);
});
return {
chartOption,
};
}

View File

@@ -0,0 +1,16 @@
import { ref } from 'vue';
export default function useLoading(initValue = false) {
const loading = ref(initValue);
const setLoading = (value: boolean) => {
loading.value = value;
};
const toggle = () => {
loading.value = !loading.value;
};
return {
loading,
setLoading,
toggle,
};
}

View File

@@ -0,0 +1,38 @@
import type { RouteLocationNormalized, RouteRecordRaw } from 'vue-router';
import { useUserStore } from '@/store';
export default function usePermission() {
const userStore = useUserStore();
return {
accessRouter(route: RouteLocationNormalized | RouteRecordRaw) {
return (
!route.meta?.requiresAuth ||
userStore.role === 'root' ||
(!route.meta?.menuCode && !route.meta?.roles) ||
Boolean(
route.meta?.menuCode &&
userStore.menuCodes.includes(route.meta.menuCode),
) ||
Boolean(route.meta?.roles?.includes(userStore.role))
);
},
findFirstPermissionRoute(_routers: any, role = userStore.role) {
const cloneRouters = [..._routers];
while (cloneRouters.length) {
const firstElement = cloneRouters.shift();
if (
role === 'root' ||
(firstElement?.meta?.menuCode &&
userStore.menuCodes.includes(firstElement.meta.menuCode)) ||
firstElement?.meta?.roles?.includes(role)
)
return { name: firstElement.name };
if (firstElement?.children) {
cloneRouters.push(...firstElement.children);
}
}
return null;
},
// You can add any rules you want
};
}

View File

@@ -0,0 +1,26 @@
import type { AxiosResponse } from 'axios';
import { ref, type UnwrapRef } from 'vue';
import type { HttpResponse } from '@/plugins/http';
import useLoading from './loading';
// use to fetch list
// Don't use async function. It doesn't work in async function.
// Use the bind function to add parameters
// example: useRequest(api.bind(null, {}))
export default function useRequest<T>(
api: () => Promise<AxiosResponse<HttpResponse>>,
defaultValue = [] as unknown as T,
isLoading = true,
) {
const { loading, setLoading } = useLoading(isLoading);
const response = ref<T>(defaultValue);
api()
.then((res) => {
response.value = res.data as unknown as UnwrapRef<T>;
})
.finally(() => {
setLoading(false);
});
return { loading, response };
}

View File

@@ -0,0 +1,32 @@
import { useDebounceFn } from '@vueuse/core';
import { onBeforeMount, onBeforeUnmount, onMounted } from 'vue';
import { useAppStore } from '@/store';
import { addEventListen, removeEventListen } from '@/utils/event';
const WIDTH = 992; // https://arco.design/vue/component/grid#responsivevalue
function queryDevice() {
const rect = document.body.getBoundingClientRect();
return rect.width - 1 < WIDTH;
}
export default function useResponsive(immediate?: boolean) {
const appStore = useAppStore();
function resizeHandler() {
if (!document.hidden) {
const isMobile = queryDevice();
appStore.toggleDevice(isMobile ? 'mobile' : 'desktop');
appStore.toggleMenu(isMobile);
}
}
const debounceFn = useDebounceFn(resizeHandler, 100);
onMounted(() => {
if (immediate) debounceFn();
});
onBeforeMount(() => {
addEventListen(window, 'resize', debounceFn);
});
onBeforeUnmount(() => {
removeEventListen(window, 'resize', debounceFn);
});
}

View File

@@ -0,0 +1,12 @@
import { computed } from 'vue';
import { useAppStore } from '@/store';
export default function useThemes() {
const appStore = useAppStore();
const isDark = computed(() => {
return appStore.theme === 'dark';
});
return {
isDark,
};
}

View File

@@ -0,0 +1,24 @@
import { Message } from '@arco-design/web-vue';
import { useRouter } from 'vue-router';
import { useUserStore } from '@/store';
export default function useUser() {
const router = useRouter();
const userStore = useUserStore();
const logout = async (logoutTo?: string) => {
await userStore.logout();
const currentRoute = router.currentRoute.value;
Message.success('登出成功');
router.push({
name: logoutTo && typeof logoutTo === 'string' ? logoutTo : 'login',
query: {
...router.currentRoute.value.query,
redirect: currentRoute.name as string,
},
});
};
return {
logout,
};
}