This commit is contained in:
xuziqiang
2024-05-15 17:29:42 +08:00
commit d0155dbe3c
7296 changed files with 1832517 additions and 0 deletions

7
lib/use/index.ts Normal file
View File

@@ -0,0 +1,7 @@
export * from './use-action';
export * from './use-api';
export * from './use-attrs';
export * from './use-params';
export * from './use-path';
export * from './use-route-config';
export * from './use-rule-event';

364
lib/use/use-action.ts Normal file
View File

@@ -0,0 +1,364 @@
import { createVNode, inject } from 'vue';
import { NsMessage } from '../component/message';
import { NsModal } from '../component/modal';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { useRouter, useRoute } from 'vue-router';
import { cloneDeep, isBoolean, isEmpty, isFunction, isString, isUndefined } from 'lodash-es';
import { useParams } from '/nerv-lib/use/use-params';
import { usePath } from '/nerv-lib/use/use-path';
import { useApi } from '/nerv-lib/use/use-api';
import type { AxiosRequestConfig } from 'axios';
import { authorizationService } from '/nerv-base/store/modules/authorization-service';
import { stringUtil } from '/nerv-lib/util/string-util';
import { appConfigStore } from '/nerv-base/store/modules/app-config';
import { xlsxExport, xlsxImport } from '/nerv-lib/util/xlsx-util';
export interface Confirm {
title: string; // 弹窗标题
content: string; // 弹窗内容
icon: string; //弹窗图标
okButtonProps: object;
okText: string; //确定按钮文本
}
export interface Action {
label: string; //操作中文名
name: string; //操作英文名
openPermission?: boolean; // true则不鉴权
checkApi?: string | Function | AxiosRequestConfig; // 检查api
checkDynamicParams?: string | Array<string> | object; //check api传参 || url传参
checkDefaultParams?: object; //check默认参数 固定值
route?: string | Recordable; // 配置了路由则直接跳转
confirm?: boolean | Confirm; //先弹窗再操作, true则使用默认显示方案
showSuccess?: Boolean;
api?: string | Function | AxiosRequestConfig; // 自动请求api
dynamicParams?: string | Array<string> | object; //api传参 || url传参
defaultParams?: object; //默认参数 固定值
handle?: Function; //自定义回调,路由模式不触发
finalHandle?: Function; //最终执行的函数
ifShow?: boolean | Function; //显示依赖
type?: string; //按钮 primary | ghost | dashed | link | text | default
dynamicDisabled?: boolean | Function;
isReload?: boolean;
state?: String; // edit 为编辑态 其他为编辑中
extra?: Recordable;
}
/**
* step1:检测route有则直接跳转。
* step2:检测api有则设置handle先行请求api请求成功有默认handle则执行。
* step3:检测confirm有则先弹窗再执行step2
*/
interface actionParams {
reload?: Function;
}
export function useAction(actionParams: actionParams = {}) {
const router = useRouter();
const route = useRoute();
const { reload } = actionParams;
const { getPath } = usePath();
const { httpRequest } = useApi();
const authService = authorizationService();
const appConfig = appConfigStore();
//todo 类型定义需简化
const { tableEdit, tableSave, tableCancel, addRow, validate, getValue, getKey, tableDelete } =
inject('tableEdit', {
tableEdit: () => {},
tableSave: () => {},
tableCancel: () => {},
addRow: () => {},
validate: () => {},
getValue: () => {},
getKey: () => {},
tableDelete: () => {},
}) as {
tableEdit: Function;
tableSave: Function;
tableCancel: Function;
addRow: Function;
validate: Function;
getValue: Function;
getKey: Function;
tableDelete: Function;
};
function isIfShow(action: Action, data: any): boolean {
const ifShow = action.ifShow;
if (isBoolean(ifShow)) return ifShow;
if (isFunction(ifShow)) return ifShow(data);
return true;
}
function hasPermission(action: Action, data: any) {
//todo 临时处理saas不鉴权
if (__APP_INFO__.serviceMode === 'saas') {
if (action.children) {
return true;
}
if (action.openPermission) return true;
return authService.checkAllPermission(action.name);
}
if (action.openPermission) return true;
if (!appConfig.actionPermission) {
return true;
}
if (isUndefined(route.name) || isUndefined(route.matched[0].name)) {
console.error('route name is required');
}
const { projectName } = route.params;
//处理IASSAction判断
if (data && data.viewInfo && Array.isArray(data.viewInfo.actions)) {
return data.viewInfo.actions.includes(action.name);
} else {
return authService.checkPermission(
route.meta?.app ? route.meta?.app : (route.matched[0].name as string),
route.meta?.bindView ? route.meta?.bindView : (route.name as string),
stringUtil.firstToLower(
action.name.replace(
route.meta?.bindView ? route.meta?.bindView : (route.name as string),
'',
),
),
data.projectName || projectName,
);
}
}
function filterAction(action: Action, data: any) {
const { state } = action;
let stateShow = true;
if (state && state !== 'add') {
if (getValue(getKey(data))) {
stateShow = state === 'edit' || isUndefined(state) ? false : true;
} else {
stateShow = state === 'edit' || isUndefined(state) ? true : false;
}
}
return stateShow && hasPermission(action, data) && isIfShow(action, data);
}
function filterActionNoAuth(action: Action, data: any) {
const { state } = action;
let stateShow = true;
if (state && state !== 'add') {
if (getValue(getKey(data))) {
stateShow = state === 'edit' || isUndefined(state) ? false : true;
} else {
stateShow = state === 'edit' || isUndefined(state) ? true : false;
}
}
return stateShow && isIfShow(action, data);
}
function transformAction(action: Action, data: any) {
const {
label,
name,
state,
route: toRoute,
api,
dynamicParams,
defaultParams,
showSuccess,
confirm,
checkApi,
checkDynamicParams,
checkDefaultParams,
handle,
isReload = false,
extra,
} = action;
const { getParams } = useParams();
const tableDynamicDisabledAction = ['deletes', 'exports']; //表格默认动态禁用操作
let { dynamicDisabled } = action;
const extraData = { router, reload, action }; // handle传出数据
if (tableDynamicDisabledAction.includes(name)) {
dynamicDisabled = (data: any) => {
return data.list.length === 0;
};
}
if (dynamicDisabled) {
if (isFunction(dynamicDisabled)) {
action.dynamicDisabled = dynamicDisabled(data);
}
} else {
action.dynamicDisabled = false;
}
if (state === 'edit') {
action.finalHandle = () => {
tableEdit(getKey(data));
};
return action;
}
if (state === 'save') {
action.finalHandle = () => {
console.log('save', getKey(data));
validate(getKey(data))
.then(() => {
tableSave(getKey(data));
})
.catch((_: any) => {});
};
return action;
}
if (state === 'cancel') {
action.finalHandle = () => {
console.log('cancel', getKey(data));
tableCancel(getKey(data));
};
return action;
}
if (state === 'delete') {
action.finalHandle = () => {
console.log("state === 'delete'", getKey(data));
tableDelete(getKey(data));
};
return action;
}
if (state === 'add') {
action.finalHandle = () => {
addRow(data);
};
return action;
}
const handleList: Recordable = {
confirm: null,
checkApi: null,
route: null,
api: null,
handle,
isReload,
};
let modelInstance: Recordable = {};
if (name && name.toLowerCase().includes('exports')) {
action.finalHandle = () => {
xlsxExport({ data: data.list, ...extra } as any);
};
return action;
}
if (name && name.toLowerCase().includes('import')) {
if (!action.handle) {
action.finalHandle = () => {
xlsxImport({ ...extra, reload } as any);
};
return action;
}
}
function routeGo(toRoute: any) {
const query = getParams(data, dynamicParams, defaultParams);
if (isString(toRoute)) {
router.push({ path: getPath(toRoute, data), query });
} else {
if (toRoute.path) {
toRoute.path = getPath(toRoute.path, data);
toRoute.query = query;
}
if (toRoute.name) {
toRoute.query = query;
}
router.push(toRoute);
}
}
if (checkApi) {
handleList.checkApi = () => {
const params = getParams(data, checkDynamicParams, checkDefaultParams);
const requestConfig: AxiosRequestConfig = { method: 'get' };
return httpRequest({ api: checkApi, params, pathParams: data, requestConfig });
};
}
if (api) {
handleList.api = () => {
const params = getParams(data, dynamicParams, defaultParams);
const requestConfig: AxiosRequestConfig = { method: 'post' };
if (__APP_INFO__.serviceMode === 'saas') {
if (requestConfig.headers) {
requestConfig.headers.resourceCode = action.name;
} else {
requestConfig.headers = {
resourceCode: action.name,
};
}
}
console.log('eeee', requestConfig);
return httpRequest({ api, params, pathParams: data, requestConfig }).then(() => {
showSuccess !== false && NsMessage.success(`${label}成功`);
});
};
}
//todo router name支持
if (toRoute) {
handleList.route = () => {
return routeGo(toRoute);
};
}
const modeUpdate = (prop: Recordable) => {
if (!isEmpty(modelInstance)) {
modelInstance.update(prop);
}
};
action.finalHandle = async () => {
modeUpdate({
okButtonProps: {
disabled: true,
},
});
if (handleList.checkApi) await handleList.checkApi();
if (handleList.route) return handleList.route();
if (handleList.api) await handleList.api();
// handleList.api && showSuccess !== false && NsMessage.success(`${label}成功`);
if (handleList.handle) await handleList.handle(data, name, { ...extraData });
if (isReload) {
isFunction(reload) && reload();
}
setTimeout(() => {
modeUpdate({
okButtonProps: {
disabled: false,
},
});
}, 100);
};
if (confirm) {
const _finalHandle: any = action.finalHandle;
const { title, content, icon, okText, okButtonProps } = confirm as Confirm;
action.finalHandle = () => {
modelInstance = NsModal.confirm({
title: title || '警告',
content: content || `确定要${label}吗?`,
icon: icon || createVNode(ExclamationCircleOutlined),
okText: okText || '确认',
cancelText: '取消',
okButtonProps,
onOk: _finalHandle,
});
};
}
return action;
}
return {
filterAction,
filterActionNoAuth,
transformAction,
};
}

59
lib/use/use-api.ts Normal file
View File

@@ -0,0 +1,59 @@
import type { AxiosRequestConfig } from 'axios';
import { http } from '../util/http';
import { isFunction, isPlainObject, isString } from 'lodash-es';
import { usePath } from '/nerv-lib/use/use-path';
export type HttpRequestConfig = AxiosRequestConfig;
export interface HttpRequest {
api: string | HttpRequestConfig | Function;
pathParams?: Recordable; // 获取动态路径
params?: Recordable;
paramsFilter?: Function;
requestConfig?: HttpRequestConfig;
}
export function useApi() {
const { getPath } = usePath();
function httpRequest({
api,
params = {},
pathParams,
paramsFilter,
requestConfig = {},
}: HttpRequest) {
if (!pathParams) {
pathParams = params;
}
let url = undefined;
if (isString(api)) {
url = getPath(api, pathParams);
} else if (isPlainObject(api)) {
requestConfig = Object.assign(requestConfig, api);
console.log('rrrrr', requestConfig);
if (requestConfig?.url) {
url = getPath(requestConfig.url, pathParams);
}
}
if (paramsFilter) {
params = paramsFilter(params);
}
if (requestConfig?.method?.toLocaleLowerCase() === 'get') {
if (!requestConfig.params) requestConfig.params = params;
} else {
if (!requestConfig.data) requestConfig.data = params;
}
if (isFunction(api)) {
return api(params);
} else {
return http.request({
...requestConfig,
url,
});
}
}
return { httpRequest };
}

41
lib/use/use-attrs.ts Normal file
View File

@@ -0,0 +1,41 @@
import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue';
import type { Ref } from 'vue';
interface Params {
excludeListeners?: boolean;
excludeKeys?: string[];
excludeDefaultKeys?: boolean;
}
const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
const LISTENER_PREFIX = /^on[A-Z]/;
export function entries<T>(obj: Recordable<T>): [string, T][] {
return Object.keys(obj).map((key: string) => [key, obj[key]]);
}
export function useAttrs(params: Params = {}): Ref<Recordable> | {} {
const instance = getCurrentInstance();
if (!instance) return {};
const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = params;
const attrs = shallowRef({});
const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []);
// Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
instance.attrs = reactive(instance.attrs);
watchEffect(() => {
const res = entries(instance.attrs).reduce((acm, [key, val]) => {
if (!allExcludeKeys.includes(key) && !(excludeListeners && LISTENER_PREFIX.test(key))) {
// console.log('key', key);
acm[key] = val;
}
return acm;
}, {} as Recordable);
attrs.value = res;
});
return attrs;
}

45
lib/use/use-navigate.ts Normal file
View File

@@ -0,0 +1,45 @@
import { useRouter } from 'vue-router';
import { onBeforeUnmount, onDeactivated, onActivated } from 'vue';
export function useNavigate() {
let isBack = false;
let isAlive = true;
const router = useRouter();
console.log(router);
function navigateBack() {
if (isAlive && !isBack) {
isBack = !isBack;
router.go(-1);
}
}
function navigateBackV2() {
if (isAlive && !isBack) {
isBack = !isBack;
const match = router.currentRoute.value.matched;
if (match.length > 1) {
const backRoute = match[match.length - 2];
router.push({ name: backRoute.name });
} else {
router.go(-1);
}
}
}
onBeforeUnmount(() => {
isAlive = false;
});
// onActivated(() => {
// isAlive = true;
// isBack = false;
// });
// onDeactivated(() => {
// isAlive = true;
// isBack = false;
// });
return {
navigateBack,
navigateBackV2,
};
}

81
lib/use/use-params.ts Normal file
View File

@@ -0,0 +1,81 @@
import { get, isArray, isFunction, isObject, isString, isUndefined } from 'lodash-es';
export function useParams() {
function getParams(
data: object,
dynamicParams?: string | Array<string> | object,
defaultParams?: object,
): any {
let paramsObject = {};
if (isString(dynamicParams)) {
paramsObject = getParamsString(data, dynamicParams);
} else if (isArray(dynamicParams)) {
paramsObject = getParamsArray(data, dynamicParams);
} else if (isFunction(dynamicParams)) {
paramsObject = getParamsFunction(data, dynamicParams);
} else if (isObject(dynamicParams)) {
// Function is Object
paramsObject = getParamsObject(data, dynamicParams);
}
return { ...defaultParams, ...paramsObject };
}
function getParamsString(data: object, dynamicParams: string, key?: string): object {
if (dynamicParams.endsWith('[]')) {
dynamicParams = dynamicParams.split('[]')[0];
if ((data as any).list) {
return {
[key || dynamicParams]: (data as any).list.map((record: any) => {
return record[dynamicParams];
}),
};
} else {
return { [key || dynamicParams]: [get(data, dynamicParams)] }; //支持单条数据数组形式
}
}
if (dynamicParams.includes('[].')) {
const dp = dynamicParams.split('[].');
const list = getParamsString(data, dp[0])[dp[0]];
if (isArray(list)) {
const rd = list.reduce((prev, cur) => {
const _data = get(cur, dp[1]);
if (!isUndefined(_data)) {
prev.push(_data);
}
return prev;
}, []);
// console.log("rd.join(',')", { [key || dynamicParams]: rd.join(',') });
return { [key || dynamicParams]: rd.join(',') }; //支持单条数据数组形式
}
}
const value = get(data, dynamicParams);
if (!isUndefined(value)) {
return { [key || dynamicParams]: get(data, dynamicParams) };
}
return {};
}
function getParamsArray(data: object, dynamicParams: Array<string>): object {
return dynamicParams.reduce((prev, cur) => {
return Object.assign(prev, getParamsString(data, cur));
}, {});
}
function getParamsObject(data: object, dynamicParams: any): object {
return Object.keys(dynamicParams).reduce((prev, cur) => {
return Object.assign(prev, getParamsString(data, dynamicParams[cur], cur));
}, {});
}
function getParamsFunction(data: object, dynamicParams: Function): object {
return dynamicParams(data);
}
function hasArrayParams(dynamicParams: Record<string, any>): boolean {
return Object.keys(dynamicParams).some((key) => {
return dynamicParams[key].endsWith('[]');
});
}
return { getParams, hasArrayParams };
}

15
lib/use/use-path.ts Normal file
View File

@@ -0,0 +1,15 @@
import { compile } from 'path-to-regexp';
export function usePath() {
function getPath(path: string, data: Recordable): string {
if (path.includes(':')) {
const toPath = compile(path, { encode: encodeURIComponent });
return toPath(data);
}
return path;
}
return {
getPath,
};
}

View File

@@ -0,0 +1,18 @@
import { useRoute } from 'vue-router';
import { computed } from 'vue';
export function useRouteConfig(props: Recordable = {}) {
const route = useRoute();
function getMeta(key?: string) {
if (key) {
return route.meta[key];
}
return route.meta;
}
const getTitle = computed(() => {
return getMeta('title') || props.title;
});
return { getMeta, getTitle };
}

44
lib/use/use-rule-event.ts Normal file
View File

@@ -0,0 +1,44 @@
import type { Ref } from 'vue';
import { ref, computed, getCurrentInstance, watch } from 'vue';
import { isEqual, isUndefined } from 'lodash-es';
interface UseRuleEvent {
emitData?: Ref<any[]>;
key: string;
changeEvent: string;
}
export function useRuleEvent({ key = 'value', changeEvent = 'change', emitData }: UseRuleEvent) {
const instance = getCurrentInstance();
const props = instance?.props;
const emit = instance?.emit;
const changeValue = ref();
const modelValue = computed({
get() {
return changeValue.value;
},
set(value) {
if (!isEqual(value, changeValue.value)) {
changeValue.value = value;
emit?.('update:value', value);
// nextTick(() => {
// emit?.(changeEvent, value, ...(emitData?.value || []));
// });
}
},
});
watch(
() => props[key],
() => {
if (!isUndefined(props[key])) {
modelValue.value = props[key];
}
},
{
immediate: true,
}
);
return { modelValue };
}