perf(projects): 优化any

This commit is contained in:
xiaojunnuo 2023-05-22 12:12:11 +08:00
parent 59af204a4c
commit f7090d3dbc
8 changed files with 197 additions and 158 deletions

View File

@ -1,21 +1,34 @@
// eslint-disable-next-line max-params
function copyList(originList: any, newList: any, options: any, parentId?: number) {
export type ListItem = {
id?: number;
children?: ListItem[];
[key: string]: any;
};
export type BaseMockOptions = { name: string; copyTimes?: number; list: ListItem[]; idGenerator: number };
type CopyListParams = { originList: ListItem[]; newList: ListItem[]; options: BaseMockOptions; parentId?: number };
function copyList(props: CopyListParams) {
const { originList, newList, options, parentId } = props;
for (const item of originList) {
const newItem = { ...item, parentId };
const newItem: ListItem = { ...item, parentId };
newItem.id = options.idGenerator;
options.idGenerator += 1;
newList.push(newItem);
if (item.children) {
newItem.children = [];
copyList(item.children, newItem.children, options, newItem.id);
copyList({
originList: item.children,
newList: newItem.children,
options,
parentId: newItem.id
});
}
}
}
function delById(req: any, list: any[]) {
function delById(req: Service.MockOption, list: any[]) {
for (let i = 0; i < list.length; i += 1) {
const item = list[i];
if (item.id === parseInt(req.params.id, 10)) {
if (item.id === parseInt(req.query.id, 10)) {
list.splice(i, 1);
break;
}
@ -25,7 +38,7 @@ function delById(req: any, list: any[]) {
}
}
function findById(id: number, list: any[]): any {
function findById(id: number, list: ListItem[]): any {
for (const item of list) {
if (item.id === id) {
return item;
@ -39,22 +52,94 @@ function findById(id: number, list: any[]): any {
}
return null;
}
function matchWithArrayCondition(value: any[], item: ListItem, key: string) {
if (value.length === 0) {
return true;
}
let matched = false;
for (const i of value) {
if (item[key] instanceof Array) {
for (const j of item[key]) {
if (i === j) {
matched = true;
break;
}
}
if (matched) {
break;
}
} else if (item[key] === i || (typeof item[key] === 'string' && item[key].indexOf(`${i}`) >= 0)) {
matched = true;
break;
}
if (matched) {
break;
}
}
return matched;
}
function matchWithObjectCondition(value: any, item: ListItem, key: string) {
let matched = true;
for (const key2 of Object.keys(value)) {
const v = value[key2];
if (v && item[key] && v !== item[key][key2]) {
matched = false;
break;
}
}
return matched;
}
function searchFromList(list: ListItem[], query: any) {
const filter = (item: ListItem) => {
let allFound = true; // 是否所有条件都符合
for (const key of Object.keys(query)) {
const value = query[key];
if (value === undefined || value === null || value === '') {
// no nothing
} else if (value instanceof Array) {
// 如果条件中的value是数组的话只要查到一个就行
const matched = matchWithArrayCondition(value, item, key);
if (!matched) {
allFound = false;
}
} else if (value instanceof Object) {
// 如果条件中的value是对象的话需要每个key都匹配
const matched = matchWithObjectCondition(value, item, key);
if (!matched) {
allFound = false;
}
} else if (item[key] !== value) {
allFound = false;
}
}
return allFound;
};
return list.filter(filter);
}
export default {
buildMock(options: { name: string; copyTimes: number; list: any[]; idGenerator: number }) {
buildMock(options: BaseMockOptions) {
const name = options.name;
if (!options.copyTimes) {
options.copyTimes = 29;
}
const list: any[] = [];
for (let i = 0; i < options.copyTimes; i += 1) {
copyList(options.list, list, options);
copyList({
originList: options.list,
newList: list,
options
});
}
options.list = list;
return [
{
path: `/mock/${name}/page`,
method: 'post',
handle(req: any) {
handle(req: Service.MockOption) {
let data = [...list];
let limit = 20;
let offset = 0;
@ -67,74 +152,15 @@ export default {
let orderAsc: any;
let orderProp: any;
if (req && req.body) {
const { page, query, sort } = req.body;
const { page, query } = req.body;
if (page.limit) {
limit = parseInt(page.limit, 10);
}
if (page.offset) {
offset = parseInt(page.offset, 10);
}
orderProp = sort.prop;
orderAsc = sort.asc;
if (Object.keys(query).length > 0) {
// eslint-disable-next-line complexity
data = list.filter(item => {
let allFound = true; // 是否所有条件都符合
// eslint-disable-next-line guard-for-in
for (const key in query) {
// 判定某一个条件
const value = query[key];
if (value === undefined || value === null || value === '') {
// eslint-disable-next-line no-continue
continue;
}
if (value instanceof Array) {
// 如果条件中的value是数组的话只要查到一个就行
if (value.length === 0) {
// eslint-disable-next-line no-continue
continue;
}
let found = false;
for (const i of value) {
if (item[key] instanceof Array) {
// eslint-disable-next-line max-depth
for (const j of item[key]) {
// eslint-disable-next-line max-depth
if (i === j) {
found = true;
break;
}
}
// eslint-disable-next-line max-depth
if (found) {
break;
}
} else if (item[key] === i || (typeof item[key] === 'string' && item[key].indexOf(`${i}`) >= 0)) {
found = true;
break;
}
if (found) {
break;
}
}
if (!found) {
allFound = false;
}
} else if (value instanceof Object) {
// eslint-disable-next-line max-depth,guard-for-in
for (const key2 in value) {
const v = value[key2];
if (v && item[key] && v !== item[key][key2]) {
return false;
}
}
} else if (item[key] !== value) {
allFound = false;
}
}
return allFound;
});
data = searchFromList(list, query);
}
}
@ -150,7 +176,7 @@ export default {
let ret = 0;
if (a[orderProp] > b[orderProp]) {
ret = 1;
} else {
} else if (a[orderProp] < b[orderProp]) {
ret = -1;
}
return orderAsc ? ret : -ret;
@ -177,8 +203,8 @@ export default {
{
path: `/mock/${name}/get`,
method: 'get',
handle(req: any) {
let id = req.params.id;
handle(req: Service.MockOption) {
let id = req.query.id;
id = parseInt(id, 10);
let current = null;
for (const item of list) {
@ -197,7 +223,7 @@ export default {
{
path: `/mock/${name}/add`,
method: 'post',
handle(req: any) {
handle(req: Service.MockOption) {
req.body.id = options.idGenerator;
options.idGenerator += 1;
list.unshift(req.body);
@ -211,7 +237,7 @@ export default {
{
path: `/mock/${name}/update`,
method: 'post',
handle(req: any) {
handle(req: Service.MockOption) {
const item = findById(req.body.id, list);
if (item) {
Object.assign(item, req.body);
@ -226,7 +252,7 @@ export default {
{
path: `/mock/${name}/delete`,
method: 'post',
handle(req: any) {
handle(req: Service.MockOption) {
delById(req, list);
return {
code: 200,
@ -238,7 +264,7 @@ export default {
{
path: `/mock/${name}/batchDelete`,
method: 'post',
handle(req: any) {
handle(req: Service.MockOption) {
const ids = req.body.ids;
for (let i = list.length - 1; i >= 0; i -= 1) {
const item = list[i];

View File

@ -1,27 +1,28 @@
import type { MethodType, MockMethod } from 'vite-plugin-mock';
import type { BaseMockOptions } from '../base';
import mockBase from '../base';
import MockOption = Service.MockOption;
const options: any = {
const options: BaseMockOptions = {
name: 'crud/demo',
idGenerator: 0
idGenerator: 0,
list: [
{
select: '1',
text: '文本测试',
copyable: '文本可复制',
avatar: 'http://greper.handsfree.work/extends/avatar.jpg',
richtext: '富文本',
datetime: '2023-01-30 11:11:11'
},
{
select: '2'
},
{
select: '0'
}
]
};
const list: any[] = [
{
select: '1',
text: '文本测试',
copyable: '文本可复制',
avatar: 'http://greper.handsfree.work/extends/avatar.jpg',
richtext: '富文本',
datetime: '2023-01-30 11:11:11'
},
{
select: '2'
},
{
select: '0'
}
];
options.list = list;
const mockedApis = mockBase.buildMock(options);
const apis: MockMethod[] = [
@ -46,7 +47,7 @@ for (const mockedApi of mockedApis) {
apis.push({
url: mockedApi.path,
method: mockedApi.method as MethodType,
response: (request: any) => {
response: (request: MockOption) => {
return mockedApi.handle(request);
}
});

View File

@ -1,33 +1,34 @@
import type { MethodType, MockMethod } from 'vite-plugin-mock';
import type { BaseMockOptions } from '../base';
import mockBase from '../base';
import MockOption = Service.MockOption;
const options: any = {
const options: BaseMockOptions = {
name: 'crud/header-group',
idGenerator: 0
idGenerator: 0,
list: [
{
name: '张三',
age: 18,
province: '广东省',
city: '深圳市',
county: '南山区',
street: '粤海街道'
},
{
name: '李四',
age: 26,
province: '浙江省',
city: '杭州市',
county: '西湖区',
street: '西湖街道'
},
{
name: '王五',
age: 24
}
]
};
const list: any[] = [
{
name: '张三',
age: 18,
province: '广东省',
city: '深圳市',
county: '南山区',
street: '粤海街道'
},
{
name: '李四',
age: 26,
province: '浙江省',
city: '杭州市',
county: '西湖区',
street: '西湖街道'
},
{
name: '王五',
age: 24
}
];
options.list = list;
const mockedApis = mockBase.buildMock(options);
const apis: MockMethod[] = [];
@ -36,7 +37,7 @@ for (const mockedApi of mockedApis) {
apis.push({
url: mockedApi.path,
method: mockedApi.method as MethodType,
response: (request: any) => {
response: (request: MockOption) => {
return mockedApi.handle(request);
}
});

View File

@ -5,6 +5,7 @@ import { FastCrud } from '@fast-crud/fast-crud';
import '@fast-crud/fast-crud/dist/style.css';
import './common.scss';
import type { FsUploaderOptions } from '@fast-crud/fast-extends';
import {
FsExtendsCopyable,
FsExtendsEditor,
@ -15,6 +16,7 @@ import {
import '@fast-crud/fast-extends/dist/style.css';
import UiNaive from '@fast-crud/ui-naive';
import axios from 'axios';
import type { VueI18n } from 'vue-i18n';
import { mockRequest, request } from '@/service/request';
import { setupNaive } from '@/plugins/fast-crud/naive';
@ -24,7 +26,10 @@ import { setupNaive } from '@/plugins/fast-crud/naive';
* @param app
* @param options
*/
function install(app: App, options: any = {}) {
export type FsSetupOpts = {
i18n?: VueI18n;
};
function install(app: App, options: FsSetupOpts = {}) {
// 安装naive ui 常用组件
setupNaive(app);
app.use(UiNaive);
@ -32,7 +37,7 @@ function install(app: App, options: any = {}) {
i18n: options.i18n,
async dictRequest(context: { url: string }) {
const url = context.url;
let res: any;
let res: Service.SuccessResult | Service.FailedResult;
if (url && url.startsWith('/mock')) {
// 如果是crud开头的dict请求视为mock
res = await mockRequest.get(url.replace('/mock', ''));
@ -44,7 +49,6 @@ function install(app: App, options: any = {}) {
},
/**
* useCrud时会被执行
* @param contextuseCrud的参数
*/
commonOptions() {
return {
@ -85,7 +89,7 @@ function install(app: App, options: any = {}) {
};
},
// page请求结果转换
transformRes: (originPageRes: { res: any; query: any }) => {
transformRes: originPageRes => {
const { res } = originPageRes;
const pageSize = res.limit;
let currentPage = res.offset / pageSize;
@ -115,29 +119,29 @@ function install(app: App, options: any = {}) {
app.use(FsExtendsJson);
app.use(FsExtendsCopyable);
// 安装uploader 公共参数
app.use(FsExtendsUploader, {
const uploaderOptions: FsUploaderOptions = {
defaultType: 'form',
form: {
action: 'http://www.docmirror.cn:7070/api/upload/form/upload',
name: 'file',
withCredentials: false,
uploadRequest: async (props: { action: string; file: File; onProgress: (progress: any) => void }) => {
uploadRequest: async props => {
const { action, file, onProgress } = props;
const data = new FormData();
data.append('file', file);
const res: any = await axios.post(action, data, {
const res = await axios.post(action, data, {
headers: {
'Content-Type': 'multipart/form-data'
},
timeout: 60000,
onUploadProgress(progress: any) {
onProgress({ percent: Math.round((progress.loaded / progress.total) * 100) });
onUploadProgress(progress) {
onProgress({ percent: Math.round((progress.loaded / progress.total!) * 100) });
}
});
// 上传完成后的结果一般返回个url 或者key,具体看你的后台返回啥
return res.data.data;
},
successHandle(ret: string) {
async successHandle(ret: string) {
// 上传完成后的结果处理, 此处应转换格式为{url:xxx,key:xxx}
return {
url: `http://www.docmirror.cn:7070${ret}`,
@ -145,7 +149,8 @@ function install(app: App, options: any = {}) {
};
}
}
} as any);
};
app.use(FsExtendsUploader, uploaderOptions);
// 安装editor
app.use(FsExtendsEditor, {
@ -161,6 +166,6 @@ export default {
install
};
export function setupFastCrud(app: App<Element>, options: any = {}) {
export function setupFastCrud(app: App<Element>, options: FsSetupOpts = {}) {
install(app, options);
}

View File

@ -1,22 +1,28 @@
import type { UserPageQuery } from '@fast-crud/fast-crud';
import { mockRequest } from '@/service/request';
const request = mockRequest;
const apiPrefix = '/crud/demo';
export type DemoRecord = {
id: number;
[key: string]: any;
};
function resHandle(res: any) {
return res.data;
}
export async function GetList(query: any) {
export async function GetList(query: UserPageQuery) {
const res = await request.post(`${apiPrefix}/page`, query);
return resHandle(res);
}
export async function AddObj(obj: any) {
export async function AddObj(obj: DemoRecord) {
const res = await request.post(`${apiPrefix}/add`, obj);
return resHandle(res);
}
export async function UpdateObj(obj: any) {
export async function UpdateObj(obj: DemoRecord) {
const res = await request.post(`${apiPrefix}/update`, obj);
return resHandle(res);
}

View File

@ -1,10 +1,10 @@
import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq } from '@fast-crud/fast-crud';
import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
import { dict } from '@fast-crud/fast-crud';
import dayjs from 'dayjs';
import * as api from './api';
export default function createCrudOptions(): CreateCrudOptionsRet {
const pageRequest = async (query: any) => {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return api.GetList(query);
};
const editRequest = async (ctx: EditReq) => {

View File

@ -1,22 +1,28 @@
import type { UserPageQuery } from '@fast-crud/fast-crud';
import { mockRequest } from '@/service/request';
const request = mockRequest;
const apiPrefix = '/crud/header-group';
export type HeaderGroupRecord = {
id: number;
[key: string]: any;
};
function resHandle(res: any) {
return res.data;
}
export async function GetList(query: any) {
export async function GetList(query: UserPageQuery) {
const res = await request.post(`${apiPrefix}/page`, query);
return resHandle(res);
}
export async function AddObj(obj: any) {
export async function AddObj(obj: HeaderGroupRecord) {
const res = await request.post(`${apiPrefix}/add`, obj);
return resHandle(res);
}
export async function UpdateObj(obj: any) {
export async function UpdateObj(obj: HeaderGroupRecord) {
const res = await request.post(`${apiPrefix}/update`, obj);
return resHandle(res);
}

View File

@ -1,22 +1,22 @@
import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
import { dict } from '@fast-crud/fast-crud';
import type { CreateCrudOptionsRet, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
import type { HeaderGroupRecord } from './api';
import * as api from './api';
export default function createCrudOptions(): CreateCrudOptionsRet {
const pageRequest = async (query: any) => {
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return api.GetList(query);
};
const editRequest = async (ctx: { form: any; row: any }) => {
const editRequest = async (ctx: { form: HeaderGroupRecord; row: HeaderGroupRecord }) => {
const { form, row } = ctx;
form.id = row.id;
return api.UpdateObj(form);
};
const delRequest = async (ctx: { row: any }) => {
const delRequest = async (ctx: { row: HeaderGroupRecord }) => {
const { row } = ctx;
return api.DelObj(row.id);
};
const addRequest = async (ctx: { form: any }) => {
const addRequest = async (ctx: { form: HeaderGroupRecord }) => {
const { form } = ctx;
return api.AddObj(form);
};
@ -69,14 +69,8 @@ export default function createCrudOptions(): CreateCrudOptionsRet {
children: {
province: {
title: '省',
search: { show: true },
type: 'dict-select',
dict: dict({
data: [
{ value: '广东省', label: '广东省' },
{ value: '浙江省', label: '浙江省' }
]
})
type: 'text',
search: { show: true }
},
city: {
title: '市',