dua
Version:
dva based data flow!
525 lines (457 loc) • 12.4 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = global || self, factory(global.Dua = {}));
}(this, (function (exports) { 'use strict';
const drive = {
normalize: (list, total) => {
const byId = list && list.reduce((prev, c) => {
if (c && c.id) {
prev[c.id] = c;
}
return prev;
}, {});
return {
byId: byId || {},
allIds: list || [],
total: typeof total === "number" ? total : list.length
};
},
transform: byId => Object.keys(byId || {}).reduce((prev, id) => prev.concat(byId[id]), []),
reduce: state => state
};
//一次遍历找出所有子节点的children
const build = (workspace, current) => {
//初始化
if (!workspace.roots) {
workspace.roots = [];
}
if (!workspace.childMap) {
workspace.childMap = {};
}
if (current.parentId) {
if (workspace.childMap[current.parentId]) {
workspace.childMap[current.parentId].push(current);
} else {
workspace.childMap[current.parentId] = [current];
}
} else {
if (!workspace.childMap.hasOwnProperty(current.id)) {
workspace.childMap[current.id] = [];
}
workspace.roots.push(current);
}
return workspace;
};
const buildRelationShip = (node, map) => {
const children = map[node.id];
children && children.forEach(element => {
buildRelationShip(element, map);
});
node.children = children;
return node;
};
const drive$1 = {
normalize: (list, total) => {
const normalized = list && list.reduce((prev, c) => {
if (c && c.id) {
prev.byId[c.id] = c;
prev.workspace = build(prev.workspace, c);
}
return prev;
}, {
byId: {},
workspace: {}
}) || {
byId: {},
workspace: {}
};
return {
byId: normalized.byId,
allIds: normalized.workspace.roots.map(i => buildRelationShip(i, normalized.workspace.childMap)),
total: typeof total === "number" ? total : list.length
};
},
transform: byId => {
const normalized = Object.keys(byId || {}).reduce((workspace, id) => build(workspace, byId[id]), {});
return normalized.workspace.roots.map(i => buildRelationShip(i, normalized.workspace.childMap));
},
reduce: state => state
};
//该算法时间复杂度是n,按层级深度先分类
const buildWorkspace = (workspace, current) => {
const depth = current.parentIds && current.parentIds.length || 0;
if (!workspace[depth]) {
workspace[depth] = {};
}
workspace[depth][current.id] = current;
return workspace;
}; //从最底层的节点开始遍历,慢慢的把节点挂到他的父节点上
const buildTree = workspace => {
const depth = workspace.length - 1;
for (let i = depth; i > 0; --i) {
Object.keys(workspace[i]).forEach(c => {
const element = workspace[i][c];
if (element.parentIds && element.parentIds.length > 0) {
const parentId = element.parentIds[0];
const parent = workspace[i - 1][parentId];
if (parent) {
if (parent.children) {
parent.children.push(element);
} else {
parent.children = [element];
}
}
}
});
} //最后取第一层,就是一个完整的树
if (workspace && workspace.length > 0) {
return Object.keys(workspace[0]).map(c => workspace[0][c]);
} else {
return [];
}
};
const drive$2 = {
normalize: (list, total) => {
const normalized = list && list.reduce((prev, c) => {
if (c && c.id) {
prev.byId[c.id] = c;
prev.workspace = buildWorkspace(prev.workspace, c);
}
return prev;
}, {
byId: {},
workspace: []
}) || {
byId: {},
workspace: []
};
return {
byId: normalized.byId,
allIds: buildTree(normalized.workspace),
total: typeof total === 'number' ? total : list.length
};
},
transform: byId => {
//这里为了防止出问题把byId里面的children给去掉
const normalized = Object.keys(byId || {}).reduce((workspace, id) => buildWorkspace(workspace, { ...byId[id],
children: undefined
}), {});
const workspace = Object.keys(normalized).map(i => normalized[i]);
return buildTree(workspace);
},
reduce: state => state
};
const drives = {};
drives["flat"] = drive;
drives["single-tree"] = drive$1;
drives["multiple-tree"] = drive$2;
var driver = {
get: id => drives[id],
clone: (id, drive) => {
const dest = drives[id];
if (dest) {
return { ...dest,
...drive
};
} else {
return null;
}
},
list: () => Object.keys(drives),
install: (id, plugin) => drives[id] = plugin
};
function create(namespace, api, option) {
const {
fetch,
fetchPart,
create,
update,
remove,
detail
} = api;
const type = option && option.type || "flat";
const drive = option && option.drive || driver.get(type);
if (!drive) {
throw new Error(`no drive for this type [${type}]`);
}
const {
transform,
normalize,
mixed
} = drive;
const reduce = drive.reduce;
return {
namespace,
state: {
byId: {},
allIds: [],
total: 0
},
effects: {
*fetch({
payload
}, {
call,
put,
select
}) {
if (fetch) {
const response = yield call(fetch, payload);
if (response && (response.code === 200 || response.code === 0)) {
//TODO这个retlist是java的,以后肯定要一致
const list = response.data.list || response.data.retlist || response.data || [];
const total = response.data.total || list.length;
const __extra__ = mixed ? yield select(state => state) : null;
yield put({
type: "onFetch",
payload: {
list,
total,
__extra__
}
});
return response || true;
}
}
return false;
},
*fetchPart({
payload
}, {
call,
put,
select
}) {
if (fetchPart) {
const response = yield call(fetchPart, payload);
if (response && (response.code === 200 || response.code === 0)) {
const list = response.data.list || response.data || [];
const total = response.data.total;
const __extra__ = mixed ? yield select(state => state) : null;
yield put({
type: "onFetchPart",
payload: {
list,
total,
__extra__
}
});
return response || true;
}
}
return false;
},
*create({
payload
}, {
call,
put,
select
}) {
if (create) {
const response = yield call(create, payload);
if (response && (response.code === 200 || response.code === 0)) {
const data = response.data;
const __extra__ = mixed ? yield select(state => state) : null;
yield put({
type: "onCreate",
payload: { ...payload,
...data,
__extra__
}
});
return response || true;
}
}
return false;
},
*update({
payload
}, {
call,
put,
select
}) {
if (update) {
const {
id,
...otherData
} = payload || {};
const response = yield call(update, id || payload, otherData);
if (response && (response.code === 200 || response.code === 0)) {
const data = response.data;
const __extra__ = mixed ? yield select(state => state) : null;
yield put({
type: "onUpdate",
payload: { ...payload,
...data,
__extra__
}
});
return response || true;
}
}
return false;
},
*remove({
payload
}, {
call,
put,
select
}) {
if (remove) {
const id = payload && payload.id || payload;
const response = yield call(remove, id);
if (response && (response.code === 200 || response.code === 0)) {
const __extra__ = mixed ? yield select(state => state) : null;
yield put({
type: "onRemove",
payload: {
id: id,
__extra__
},
__extra__
});
return response || true;
}
}
return false;
},
*detail({
payload
}, {
call,
put,
select
}) {
if (detail) {
// const id = (payload && payload.id) || payload;
const response = yield call(detail, payload);
if (response && (response.code === 200 || response.code === 0)) {
const data = response.data;
const __extra__ = mixed ? yield select(state => state) : null;
yield put({
type: "onUpdate",
payload: data,
__extra__
});
return response || true;
}
}
return false;
}
},
reducers: {
onFetch(state, {
payload
}) {
const {
list,
total,
__extra__
} = payload;
const normalized = normalize(list, total);
return reduce({ ...state,
...normalized
}, __extra__);
},
onFetchPart(state, {
payload
}) {
const {
list,
total,
__extra__
} = payload;
function getListByTree(tree) {
return tree.reduce((prev, c) => {
if (c.children) {
prev = prev.concat(getListByTree(c.children));
}
prev = prev.concat({ ...c,
children: undefined
});
return prev;
}, []);
}
const treeList = getListByTree(state.allIds);
const normalized = normalize([].concat(treeList, list), total ? total : state.total + list.length);
return reduce({ ...state,
...normalized
}, __extra__);
},
onCreate(state, {
payload
}) {
if (payload && payload.id) {
const byId = { ...state.byId,
[payload.id]: payload
};
const allIds = transform(byId);
return reduce({ ...state,
byId,
allIds,
total: state.total + 1
}, payload.__extra__);
} else {
return state;
}
},
onUpdate(state, {
payload
}) {
if (payload) {
if (payload.id) {
const byId = { ...state.byId,
[payload.id]: { ...state.byId[payload.id],
...payload
}
};
const allIds = transform(byId);
return reduce({ ...state,
byId,
allIds
}, payload.__extra__);
} else {
//如果没有ID的话给他一个固定ID(__id__)
const byId = { ...state.byId,
__auto_id__: { ...state.byId[payload.id],
...payload
}
};
const allIds = transform(byId);
return reduce({ ...state,
byId,
allIds
}, payload.__extra__);
}
} else {
return state;
}
},
onRemove(state, {
payload
}) {
if (payload && payload.id) {
const byId = { ...state.byId
};
delete byId[payload.id];
const allIds = transform(byId);
return reduce({ ...state,
byId,
allIds,
total: state.total - 1
}, payload.__extra__);
} else {
return state;
}
}
}
};
}
exports.create = create;
exports.driver = driver;
Object.defineProperty(exports, '__esModule', { value: true });
})));