initHeader

This commit is contained in:
2024-08-22 11:27:06 +08:00
parent fe895bd37c
commit e0aaa7a30d
178 changed files with 5726 additions and 4999 deletions

View File

@@ -0,0 +1,32 @@
export namespace Table {
export interface Pageable {
pageNum: number;
pageSize: number;
total: number;
}
export interface StateProps {
tableData: any[];
pageable: Pageable;
searchParam: {
[key: string]: any;
};
searchInitParam: {
[key: string]: any;
};
totalParam: {
[key: string]: any;
};
icon?: {
[key: string]: any;
};
}
}
export namespace HandleData {
export type MessageType = "" | "success" | "warning" | "info" | "error";
}
export namespace Theme {
export type ThemeType = "light" | "inverted" | "dark";
export type GreyOrWeakType = "grey" | "weak";
}

View File

@@ -0,0 +1,22 @@
import { computed } from "vue";
import { useRoute } from "vue-router";
import { useAuthStore } from "@/stores/modules/auth";
/**
* @description 页面按钮权限
* */
export const useAuthButtons = () => {
const route = useRoute();
const authStore = useAuthStore();
const authButtons = authStore.authButtonListGet[route.name as string] || [];
const BUTTONS = computed(() => {
let currentPageAuthButton: { [key: string]: boolean } = {};
authButtons.forEach(item => (currentPageAuthButton[item] = true));
return currentPageAuthButton;
});
return {
BUTTONS
};
};

View File

@@ -0,0 +1,44 @@
import { ElNotification } from "element-plus";
/**
* @description 接收数据流生成 blob创建链接下载文件
* @param {Function} api 导出表格的api方法 (必传)
* @param {String} tempName 导出的文件名 (必传)
* @param {Object} params 导出的参数 (默认{})
* @param {Boolean} isNotify 是否有导出消息提示 (默认为 true)
* @param {String} fileType 导出的文件格式 (默认为.xlsx)
* */
export const useDownload = async (
api: (param: any) => Promise<any>,
tempName: string,
params: any = {},
isNotify: boolean = true,
fileType: string = ".xlsx"
) => {
if (isNotify) {
ElNotification({
title: "温馨提示",
message: "如果数据庞大会导致下载缓慢哦,请您耐心等待!",
type: "info",
duration: 3000
});
}
try {
const res = await api(params);
const blob = new Blob([res]);
// 兼容 edge 不支持 createObjectURL 方法
if ("msSaveOrOpenBlob" in navigator) return window.navigator.msSaveOrOpenBlob(blob, tempName + fileType);
const blobUrl = window.URL.createObjectURL(blob);
const exportFile = document.createElement("a");
exportFile.style.display = "none";
exportFile.download = `${tempName}${fileType}`;
exportFile.href = blobUrl;
document.body.appendChild(exportFile);
exportFile.click();
// 去除下载对 url 的影响
document.body.removeChild(exportFile);
window.URL.revokeObjectURL(blobUrl);
} catch (error) {
console.log(error);
}
};

View File

@@ -0,0 +1,34 @@
import { ElMessageBox, ElMessage } from "element-plus";
import { HandleData } from "./interface";
/**
* @description 操作单条数据信息 (二次确认【删除、禁用、启用、重置密码】)
* @param {Function} api 操作数据接口的api方法 (必传)
* @param {Object} params 携带的操作数据参数 {id,params} (必传)
* @param {String} message 提示信息 (必传)
* @param {String} confirmType icon类型 (不必传,默认为 warning)
* @returns {Promise}
*/
export const useHandleData = (
api: (params: any) => Promise<any>,
params: any = {},
message: string,
confirmType: HandleData.MessageType = "warning"
) => {
return new Promise((resolve, reject) => {
ElMessageBox.confirm(`是否${message}?`, "温馨提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: confirmType,
draggable: true
}).then(async () => {
const res = await api(params);
if (!res) return reject(false);
ElMessage({
type: "success",
message: `${message}成功!`
});
resolve(true);
});
});
};

View File

@@ -0,0 +1,27 @@
import { ref, onMounted, onUnmounted } from "vue";
/**
* @description 网络是否可用
* */
export const useOnline = () => {
const online = ref(true);
const showStatus = (val: any) => {
online.value = typeof val == "boolean" ? val : val.target.online;
};
// 在页面加载后,设置正确的网络状态
navigator.onLine ? showStatus(true) : showStatus(false);
onMounted(() => {
// 开始监听网络状态的变化
window.addEventListener("online", showStatus);
window.addEventListener("offline", showStatus);
});
onUnmounted(() => {
// 移除监听网络状态的变化
window.removeEventListener("online", showStatus);
window.removeEventListener("offline", showStatus);
});
return { online };
};

View File

@@ -0,0 +1,34 @@
import { ref, computed } from "vue";
/**
* @description 表格多选数据操作
* @param {String} rowKey 当表格可以多选时,所指定的 id
* */
export const useSelection = (rowKey: string = "id") => {
const isSelected = ref<boolean>(false);
const selectedList = ref<{ [key: string]: any }[]>([]);
// 当前选中的所有 ids 数组
const selectedListIds = computed((): string[] => {
let ids: string[] = [];
selectedList.value.forEach(item => ids.push(item[rowKey]));
return ids;
});
/**
* @description 多选操作
* @param {Array} rowArr 当前选择的所有数据
* @return void
*/
const selectionChange = (rowArr: { [key: string]: any }[]) => {
rowArr.length ? (isSelected.value = true) : (isSelected.value = false);
selectedList.value = rowArr;
};
return {
isSelected,
selectedList,
selectedListIds,
selectionChange
};
};

View File

@@ -0,0 +1,154 @@
import { Table } from "./interface";
import { reactive, computed, toRefs } from "vue";
/**
* @description table 页面操作方法封装
* @param {Function} api 获取表格数据 api 方法 (必传)
* @param {Object} initParam 获取数据初始化参数 (非必传,默认为{})
* @param {Boolean} isPageable 是否有分页 (非必传默认为true)
* @param {Function} dataCallBack 对后台返回的数据进行处理的方法 (非必传)
* */
export const useTable = (
api?: (params: any) => Promise<any>,
initParam: object = {},
isPageable: boolean = true,
dataCallBack?: (data: any) => any,
requestError?: (error: any) => void
) => {
const state = reactive<Table.StateProps>({
// 表格数据
tableData: [],
// 分页数据
pageable: {
// 当前页数
pageNum: 1,
// 每页显示条数
pageSize: 10,
// 总条数
total: 0
},
// 查询参数(只包括查询)
searchParam: {},
// 初始化默认的查询参数
searchInitParam: {},
// 总参数(包含分页和查询参数)
totalParam: {}
});
/**
* @description 分页查询参数(只包括分页和表格字段排序,其他排序方式可自行配置)
* */
const pageParam = computed({
get: () => {
return {
pageNum: state.pageable.pageNum,
pageSize: state.pageable.pageSize
};
},
set: (newVal: any) => {
console.log("我是分页更新之后的值", newVal);
}
});
/**
* @description 获取表格数据
* @return void
* */
const getTableList = async () => {
if (!api) return;
try {
// 先把初始化参数和分页参数放到总参数里面
Object.assign(state.totalParam, initParam, isPageable ? pageParam.value : {});
let { data } = await api({ ...state.searchInitParam, ...state.totalParam });
dataCallBack && (data = dataCallBack(data));
state.tableData = isPageable ? data.list : data;
// 解构后台返回的分页数据 (如果有分页更新分页信息)
if (isPageable) {
const { pageNum, pageSize, total } = data;
updatePageable({ pageNum, pageSize, total });
}
} catch (error) {
requestError && requestError(error);
}
};
/**
* @description 更新查询参数
* @return void
* */
const updatedTotalParam = () => {
state.totalParam = {};
// 处理查询参数,可以给查询参数加自定义前缀操作
let nowSearchParam: Table.StateProps["searchParam"] = {};
// 防止手动清空输入框携带参数(这里可以自定义查询参数前缀)
for (let key in state.searchParam) {
// 某些情况下参数为 false/0 也应该携带参数
if (state.searchParam[key] || state.searchParam[key] === false || state.searchParam[key] === 0) {
nowSearchParam[key] = state.searchParam[key];
}
}
Object.assign(state.totalParam, nowSearchParam, isPageable ? pageParam.value : {});
};
/**
* @description 更新分页信息
* @param {Object} pageable 后台返回的分页数据
* @return void
* */
const updatePageable = (pageable: Table.Pageable) => {
Object.assign(state.pageable, pageable);
};
/**
* @description 表格数据查询
* @return void
* */
const search = () => {
state.pageable.pageNum = 1;
updatedTotalParam();
getTableList();
};
/**
* @description 表格数据重置
* @return void
* */
const reset = () => {
state.pageable.pageNum = 1;
// 重置搜索表单的时,如果有默认搜索参数,则重置默认的搜索参数
state.searchParam = { ...state.searchInitParam };
updatedTotalParam();
getTableList();
};
/**
* @description 每页条数改变
* @param {Number} val 当前条数
* @return void
* */
const handleSizeChange = (val: number) => {
state.pageable.pageNum = 1;
state.pageable.pageSize = val;
getTableList();
};
/**
* @description 当前页改变
* @param {Number} val 当前页
* @return void
* */
const handleCurrentChange = (val: number) => {
state.pageable.pageNum = val;
getTableList();
};
return {
...toRefs(state),
getTableList,
search,
reset,
handleSizeChange,
handleCurrentChange,
updatedTotalParam
};
};

View File

@@ -0,0 +1,111 @@
import { storeToRefs } from "pinia";
import { Theme } from "./interface";
import { ElMessage } from "element-plus";
import { DEFAULT_PRIMARY } from "@/config";
import { useGlobalStore } from "@/stores/modules/global";
import { getLightColor, getDarkColor } from "@/utils/color";
import { menuTheme } from "@/styles/theme/menu";
import { asideTheme } from "@/styles/theme/aside";
import { headerTheme } from "@/styles/theme/header";
/**
* @description 全局主题 hooks
* */
export const useTheme = () => {
const globalStore = useGlobalStore();
const { primary, isDark, isGrey, isWeak, layout, asideInverted, headerInverted } = storeToRefs(globalStore);
// 切换暗黑模式 ==> 同时修改主题颜色、侧边栏、头部颜色
const switchDark = () => {
const html = document.documentElement as HTMLElement;
if (isDark.value) html.setAttribute("class", "dark");
else html.setAttribute("class", "");
changePrimary(primary.value);
setAsideTheme();
setHeaderTheme();
};
// 修改主题颜色
const changePrimary = (val: string | null) => {
if (!val) {
val = DEFAULT_PRIMARY;
ElMessage({ type: "success", message: `主题颜色已重置为 ${DEFAULT_PRIMARY}` });
}
// 计算主题颜色变化
document.documentElement.style.setProperty("--el-color-primary", val);
document.documentElement.style.setProperty(
"--el-color-primary-dark-2",
isDark.value ? `${getLightColor(val, 0.2)}` : `${getDarkColor(val, 0.3)}`
);
for (let i = 1; i <= 9; i++) {
const primaryColor = isDark.value ? `${getDarkColor(val, i / 10)}` : `${getLightColor(val, i / 10)}`;
document.documentElement.style.setProperty(`--el-color-primary-light-${i}`, primaryColor);
}
globalStore.setGlobalState("primary", val);
};
// 灰色和弱色切换
const changeGreyOrWeak = (type: Theme.GreyOrWeakType, value: boolean) => {
const body = document.body as HTMLElement;
if (!value) return body.removeAttribute("style");
const styles: Record<Theme.GreyOrWeakType, string> = {
grey: "filter: grayscale(1)",
weak: "filter: invert(80%)"
};
body.setAttribute("style", styles[type]);
const propName = type === "grey" ? "isWeak" : "isGrey";
globalStore.setGlobalState(propName, false);
};
// 设置菜单样式
const setMenuTheme = () => {
let type: Theme.ThemeType = "light";
if (layout.value === "transverse" && headerInverted.value) type = "inverted";
if (layout.value !== "transverse" && asideInverted.value) type = "inverted";
if (isDark.value) type = "dark";
const theme = menuTheme[type!];
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
};
// 设置侧边栏样式
const setAsideTheme = () => {
let type: Theme.ThemeType = "light";
if (asideInverted.value) type = "inverted";
if (isDark.value) type = "dark";
const theme = asideTheme[type!];
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
setMenuTheme();
};
// 设置头部样式
const setHeaderTheme = () => {
let type: Theme.ThemeType = "light";
if (headerInverted.value) type = "inverted";
if (isDark.value) type = "dark";
const theme = headerTheme[type!];
for (const [key, value] of Object.entries(theme)) {
document.documentElement.style.setProperty(key, value);
}
setMenuTheme();
};
// init theme
const initTheme = () => {
switchDark();
if (isGrey.value) changeGreyOrWeak("grey", true);
if (isWeak.value) changeGreyOrWeak("weak", true);
};
return {
initTheme,
switchDark,
changePrimary,
changeGreyOrWeak,
setAsideTheme,
setHeaderTheme
};
};

View File

@@ -0,0 +1,38 @@
import { ref } from "vue";
/**
* @description 获取本地时间
*/
export const useTime = () => {
const year = ref(0); // 年份
const month = ref(0); // 月份
const week = ref(""); // 星期几
const day = ref(0); // 天数
const hour = ref<number | string>(0); // 小时
const minute = ref<number | string>(0); // 分钟
const second = ref<number | string>(0); // 秒
const nowTime = ref<string>(""); // 当前时间
// 更新时间
const updateTime = () => {
const date = new Date();
year.value = date.getFullYear();
month.value = date.getMonth() + 1;
week.value = "日一二三四五六".charAt(date.getDay());
day.value = date.getDate();
hour.value =
(date.getHours() + "")?.padStart(2, "0") ||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getHours());
minute.value =
(date.getMinutes() + "")?.padStart(2, "0") ||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getMinutes());
second.value =
(date.getSeconds() + "")?.padStart(2, "0") ||
new Intl.NumberFormat(undefined, { minimumIntegerDigits: 2 }).format(date.getSeconds());
nowTime.value = `${year.value}${month.value}${day.value} ${hour.value}:${minute.value}:${second.value}`;
};
updateTime();
return { year, month, day, hour, minute, second, week, nowTime };
};