提交 2e8374a7 authored 作者: vipcxj's avatar vipcxj

Merge remote-tracking branch 'scaffold/master'

# Conflicts: # .roadhogrc.js # package.json # src/models/domain.js # src/models/main/index.js # src/router.js # src/routes/domain/index.js # src/routes/main/header.js 更新到最新的脚手架
{
"presets": ["./tools/babel-preset"]
}
{
"parser": "babel-eslint",
"extends": "airbnb",
"extends": ["airbnb", "plugin:lodash-fp/recommended"],
"rules": {
"generator-star-spacing": [0],
"consistent-return": [0],
......@@ -25,11 +25,27 @@
"require-yield": [1],
"no-plusplus": "off",
"no-mixed-operators": "off",
"max-len": [0, 120]
"max-len": [0, 120],
"object-curly-newline": "off",
"padded-blocks": "off",
"function-paren-newline": "off",
"jsx-a11y/anchor-is-valid": "off",
"no-multi-assign": "off",
"no-await-in-loop": "off",
"prefer-destructuring": "off",
"lines-between-class-members": "off",
"no-multiple-empty-lines": "off",
"react/require-default-props": "off",
"react/no-array-index-key": "off",
"react/destructuring-assignment": "off",
"react/jsx-one-expression-per-line": "off",
"lodash-fp/use-fp": "off",
"lodash-fp/no-unused-result": "off"
},
"parserOptions": {
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
}
},
"plugins": ["lodash-fp"]
}
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
/.cache-loader
# dependencies
/node_modules
......@@ -10,5 +12,8 @@
.DS_Store
npm-debug.log*
/.idea
/storybook-static
jsCodeStructure.html
/webpack/**
!/webpack/.gitkeep
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
\ No newline at end of file
const proxy = {
"target": "http://192.168.1.22:8080",
"changeOrigin": true,
"pathRewrite": {
"^/api": "/app/api"
}
};
const resource_proxy = {
"target": "http://192.168.1.22:8080",
"changeOrigin": true,
"pathRewrite": {
"^/resource": "/app/resource"
}
};
module.exports = {
"entry": "src/index.js",
"theme": {
"layout-header-height": "48px",
"layout-header-background": "#fff",
"layout-footer-background": "#fff",
"layout-sider-background": '#404040',
'menu-dark-bg': '#404040',
"layout-header-padding": "0",
},
"env": {
"development": {
"extraBabelPlugins": [
"dva-hmr",
"transform-runtime",
["import", { "libraryName": "antd", "style": true }]
],
"proxy": {
"/api": proxy,
"/resource": resource_proxy,
}
},
"production": {
publicPath: "/app/console/",
define: {
contextPath: "/app/console",
apiContextPath: "/app"
},
"extraBabelPlugins": [
"transform-runtime",
["import", { "libraryName": "antd", "style": true }]
]
}
}
};
const oldRequire = require.extensions['.js'];
const babelRegister = require('./babel-register');
const babelRequire = require.extensions['.js'];
require.extensions = (m, filename) => {
oldRequire(m, filename);
delete require.cache[filename];
return babelRequire(m, filename);
};
module.exports = require('./mock');
babelRegister.unregister();
{
"presets": [
"es2015",
"es2016",
"es2017",
"stage-0",
"react"
],
"plugins": [
"dva-hmr",
"transform-runtime",
["import", { "libraryName": "antd", "style": true }]
]
"presets": ["../tools/babel-preset"]
}
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
import 'babel-polyfill';
import '@babel/polyfill';
import { configure } from '@storybook/react';
function loadStories() {
require('../stories');
}
configure(loadStories, module);
import('../stories').then((stories) => {
configure(() => stories, module);
});
require('../babel-register');
const createProxy = require('http-proxy-middleware');
const _ = require('lodash');
const assert = require('assert');
const roadhog = require('../.roadhogrc');
const mock = require('../.roadhogrc.mock');
function parseKey(key) {
let method = 'get';
let path = key;
if (key.indexOf(' ') > -1) {
const splited = key.split(' ');
method = splited[0].toLowerCase();
path = splited[1];
}
return { method, path };
}
function makeProxy(method, path, target) {
const filter = (pathname, req) => {
return path.test(pathname) && req.method === method.toUpperCase();
};
return createProxy(filter, { target });
}
function createMockHandler(value) {
return function mockHandler(...args) {
const res = args[1];
if (typeof value === 'function') {
value(...args);
} else {
res.json(value);
}
};
}
const forEach = require('lodash/forEach');
require('../babel-register');
const proxy = require('../proxy');
const mock = require('../mock');
const applyMock = require('../tools/applyMock');
module.exports = (router) => {
const proxy = roadhog.env.development.proxy;
_.forEach(proxy, (value, key) => {
router.use(key, createProxy(key, value));
module.exports = (app) => {
forEach(proxy, (value, key) => {
app.use(key, createProxy(key, value));
});
_.forEach(mock, (value, key) => {
const parsedkey = parseKey(key);
assert(
typeof value === 'function' ||
typeof value === 'object' ||
typeof value === 'string',
`mock value of ${key} should be function or object or string, but got ${typeof value}`
);
if (typeof value === 'string') {
let path = parsedkey.path;
if (/\(.+\)/.test(parsedkey.path)) {
path = new RegExp(`^${parsedkey.path}$`);
}
router.use(
path,
makeProxy(parsedkey.method, path, value)
);
} else {
router[parsedkey.method](
parsedkey.path,
createMockHandler(value)
);
}
})
applyMock(app, mock);
};
const genDefaultConfig = require('@storybook/react/dist/server/config/defaults/webpack.config.js');
const path = require('path');
let config;
module.exports = (baseConfig, env) => {
const config = genDefaultConfig(baseConfig, env);
// Extend it as you need.
if (process.env.NODE_ENV === 'production') {
config = require('../webpack.prod');
} else {
config = require('../webpack.dev');
}
// For example, add typescript loader:
config.module.rules.push({
test: /\.less$/,
include: path.resolve(__dirname, '../src'),
use: [
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
options: {
modules: true,
},
},
require.resolve('less-loader')
],
}, {
test: /\.less$/,
exclude: path.resolve(__dirname, '../src'),
use: [
require.resolve('style-loader'),
require.resolve('css-loader'),
require.resolve('less-loader')
],
});
return config;
module.exports = (baseConfig, env) => {
baseConfig.resolve.extensions = config.resolve.extensions;
baseConfig.module.rules = config.module.rules;
return baseConfig;
};
/* eslint-disable comma-dangle */
const babel = require('@babel/register/lib/node');
const path = require('path');
const babel = require('./my-babel-register');
babel.default(({
babel.default({
presets: [
'es2015',
'react'
['./tools/babel-preset', {
modules: 'commonjs',
}],
],
babelrc: false,
only: [
path.resolve(__dirname, 'src/**/*'),
path.resolve(__dirname, 'stories/**/*'),
path.resolve(__dirname, 'mock/**/*'),
]
}));
});
// require('@babel/runtime');
module.exports = {
unregister() {
babel.hookExtensions([]);
babel.default({
extensions: [],
});
},
};
/* eslint-disable */
exports.__esModule = true;
var _stringify = require("babel-runtime/core-js/json/stringify");
var _stringify2 = _interopRequireDefault(_stringify);
exports.save = save;
exports.load = load;
exports.get = get;
var _path = require("path");
var _path2 = _interopRequireDefault(_path);
var _fs = require("fs");
var _fs2 = _interopRequireDefault(_fs);
var _mkdirp = require("mkdirp");
var _homeOrTmp = require("home-or-tmp");
var _homeOrTmp2 = _interopRequireDefault(_homeOrTmp);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var FILENAME = process.env.BABEL_CACHE_PATH || _path2.default.join(_homeOrTmp2.default, ".babel.json");
var data = {};
function save() {
var serialised = "{}";
try {
serialised = (0, _stringify2.default)(data, null, " ");
} catch (err) {
if (err.message === "Invalid string length") {
err.message = "Cache too large so it's been cleared.";
console.error(err.stack);
} else {
throw err;
}
}
(0, _mkdirp.sync)(_path2.default.dirname(FILENAME));
_fs2.default.writeFileSync(FILENAME, serialised);
}
function load() {
if (process.env.BABEL_DISABLE_CACHE) return;
process.on("exit", save);
process.nextTick(save);
if (!_fs2.default.existsSync(FILENAME)) return;
try {
data = JSON.parse(_fs2.default.readFileSync(FILENAME));
} catch (err) {
return;
}
}
function get() {
return data;
}
const makeSureEndsWithSlash = (path) => {
if (!path || !path.endsWith('/')) {
return `${path || ''}/`;
} else {
return path;
}
};
const makeSureStartsWithSlash = (path) => {
if (!path || !path.startsWith('/')) {
return `/${path || ''}`;
} else {
return path;
}
};
const tripEndSlash = (path) => {
if (path && path.endsWith('/')) {
return path.slice(0, path.length - 1);
} else {
return path;
}
};
const contextPathDev = '';
const contextPathProd = 'bm/console';
const apiContextPathDev = '';
const apiContextPathProd = 'bm';
module.exports = {
dev: {
publicPath: makeSureEndsWithSlash(makeSureStartsWithSlash(contextPathDev)),
basename: tripEndSlash(makeSureStartsWithSlash(contextPathDev)),
contextPath: tripEndSlash(makeSureStartsWithSlash(contextPathDev)),
apiContextPath: tripEndSlash(makeSureStartsWithSlash(apiContextPathDev)),
},
prod: {
publicPath: makeSureEndsWithSlash(makeSureStartsWithSlash(contextPathProd)),
basename: tripEndSlash(makeSureStartsWithSlash(contextPathProd)),
contextPath: tripEndSlash(makeSureStartsWithSlash(contextPathProd)),
apiContextPath: tripEndSlash(makeSureStartsWithSlash(apiContextPathProd)),
},
};
/* eslint-disable no-shadow */
import moment from 'moment';
import { genModules } from './src/mock/modules';
import { getTasks } from './src/mock/tasks';
import toFilters from './src/mock/filter';
import { genModules } from '../src/mock/modules';
import { getTasks } from '../src/mock/tasks';
import toFilters from '../src/mock/filter';
const modules = genModules();
const tasks = getTasks();
const users = [
{
userId: 1,
userName: 'cxj',
tokenId: '12345-65432-abcdef',
expire: -1,
},
];
let currentUser = users[0];
const domains = [
{
id: 1,
......@@ -47,8 +36,7 @@ const getDomain = (id) => {
};
const dealWithData = (req) => {
const sort = req.query.sort;
const order = req.query.order;
const { sort, order } = req.query;
const filters = [];
for (const queryKey in req.query) {
if (queryKey.indexOf('f-') === 0) {
......@@ -114,7 +102,7 @@ module.exports = {
},
'/api/domain/all': wrapResponse(domains),
'/api/domain/switch': (req, res) => {
const domainId = req.query.domainId;
const { domainId } = req.query;
const intDomainId = parseInt(domainId, 10);
const domainIds = domains.map(domain => domain.id);
if (domainIds.indexOf(intDomainId) !== -1) {
......@@ -136,7 +124,7 @@ module.exports = {
},
'/api/module/all/info': (req, res) => {
console.log('/api/module/all/info');
const all = modules.all;
const { all } = modules;
const publics = modules.public;
const findModule = id => all.filter(m => m.id === id).pop();
const fetchParent = (module) => {
......
/* eslint-disable */
exports.__esModule = true;
var _keys = require("babel-runtime/core-js/object/keys");
var _keys2 = _interopRequireDefault(_keys);
var _stringify = require("babel-runtime/core-js/json/stringify");
var _stringify2 = _interopRequireDefault(_stringify);
exports.default = function () {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
if (opts.only != null) only = _babelCore.util.arrayify(opts.only, _babelCore.util.regexify);
if (opts.ignore != null) ignore = _babelCore.util.arrayify(opts.ignore, _babelCore.util.regexify);
if (opts.extensions) hookExtensions(_babelCore.util.arrayify(opts.extensions));
if (opts.cache === false) cache = null;
delete opts.extensions;
delete opts.ignore;
delete opts.cache;
delete opts.only;
(0, _extend2.default)(transformOpts, opts);
};
var _cloneDeep = require("lodash/cloneDeep");
var _cloneDeep2 = _interopRequireDefault(_cloneDeep);
var _sourceMapSupport = require("source-map-support");
var _sourceMapSupport2 = _interopRequireDefault(_sourceMapSupport);
var _cache = require("./cache");
var registerCache = _interopRequireWildcard(_cache);
var _extend = require("lodash/extend");
var _extend2 = _interopRequireDefault(_extend);
var _babelCore = require("babel-core");
var babel = _interopRequireWildcard(_babelCore);
var _fs = require("fs");
var _fs2 = _interopRequireDefault(_fs);
var _path = require("path");
var _path2 = _interopRequireDefault(_path);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
_sourceMapSupport2.default.install({
handleUncaughtExceptions: false,
environment: "node",
retrieveSourceMap: function retrieveSourceMap(source) {
var map = maps && maps[source];
if (map) {
return {
url: null,
map: map
};
} else {
return null;
}
}
});
registerCache.load();
var cache = registerCache.get();
var transformOpts = {};
var ignore = void 0;
var only = void 0;
var oldHandlers = {};
var maps = {};
var cwd = process.cwd();
function getRelativePath(filename) {
return _path2.default.relative(cwd, filename);
}
function mtime(filename) {
return +_fs2.default.statSync(filename).mtime;
}
function compile(filename) {
var result = void 0;
var opts = new _babelCore.OptionManager().init((0, _extend2.default)({ sourceRoot: _path2.default.dirname(filename) }, (0, _cloneDeep2.default)(transformOpts), { filename: filename }));
var cacheKey = (0, _stringify2.default)(opts) + ":" + babel.version;
var env = process.env.BABEL_ENV || process.env.NODE_ENV;
if (env) cacheKey += ":" + env;
if (cache) {
var cached = cache[cacheKey];
if (cached && cached.mtime === mtime(filename)) {
result = cached;
}
}
if (!result) {
result = babel.transformFileSync(filename, (0, _extend2.default)(opts, {
babelrc: false,
sourceMaps: "both",
ast: false
}));
}
if (cache) {
cache[cacheKey] = result;
result.mtime = mtime(filename);
}
maps[filename] = result.map;
return result.code;
}
function shouldIgnore(filename) {
if (!ignore && !only) {
return getRelativePath(filename).split(_path2.default.sep).indexOf("node_modules") >= 0;
} else {
return _babelCore.util.shouldIgnore(filename, ignore || [], only);
}
}
function loader(m, filename) {
m._compile(compile(filename), filename);
}
function registerExtension(ext) {
var old = oldHandlers[ext] || oldHandlers[".js"] || require.extensions[".js"];
require.extensions[ext] = function (m, filename) {
if (shouldIgnore(filename)) {
old(m, filename);
} else {
loader(m, filename, old);
}
};
}
function hookExtensions(_exts) {
(0, _keys2.default)(oldHandlers).forEach(function (ext) {
var old = oldHandlers[ext];
if (old === undefined) {
delete require.extensions[ext];
} else {
require.extensions[ext] = old;
}
});
oldHandlers = {};
_exts.forEach(function (ext) {
oldHandlers[ext] = require.extensions[ext];
registerExtension(ext);
});
}
exports.hookExtensions = hookExtensions;
exports.exts = _babelCore.util.canCompile.EXTENSIONS;
hookExtensions(exports.exts);
module.exports = exports;
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"private": true,
"scripts": {
"start": "roadhog server",
"build": "roadhog build",
"start": "cross-env NODE_ENV=development webpack-dev-server --open --config webpack.dev.js",
"build": "cross-env NODE_ENV=production webpack --config webpack.prod.js",
"test": "roadhog test --require ./test_setup.js",
"lint": "eslint --ext .js src test",
"precommit": "npm run lint",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
"storybook": "cross-env NODE_ENV=development start-storybook -p 6006",
"build-storybook": "cross-env NODE_ENV=production build-storybook"
},
"engines": {
"install-node": "6.9.2"
},
"browserslist": [
"ie >= 9"
],
"sideEffects": ["src/polyfill.js"],
"dependencies": {
"antd": "^2.13.0",
"axo": "0.0.2",
"babel-plugin-import": "^1.2.1",
"babel-runtime": "^6.9.2",
"bowser": "^1.8.1",
"bundle-loader": "^0.5.5",
"dva": "^1.2.1",
"dva-loading": "^0.2.1",
"fastjson_ref_resolver": "latest",
"@babel/polyfill": "^7.0.0",
"@babel/runtime": "^7.1.2",
"antd": "^3.9.2",
"axo": "^0.0.2",
"bowser": "^1.9.4",
"classnames": "^2.2.6",
"dva": "^2.4.1",
"dva-loading": "^2.0.6",
"fastjson_ref_resolver": "^0.0.3",
"fingerprintjs": "^0.5.3",
"lodash": "^4.17.4",
"github-markdown-css": "^2.10.0",
"highlight.js": "^9.12.0",
"history": "^4.7.2",
"hoist-non-react-statics": "^3.0.1",
"is-promise": "^2.1.0",
"lodash": "^4.17.10",
"lowdb": "^1.0.0",
"moment": "^2.18.1",
"prop-types": "^15.5.10",
"moment": "^2.22.2",
"prop-types": "^15.6.2",
"qrcode": "^1.0.0",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-json-view": "^1.11.4",
"react-sizeme": "^2.3.4",
"resolve-pathname": "^2.1.0",
"uuid": "^3.1.0",
"react": "^16.5.2",
"react-async-wrapper": "^1.0.6",
"react-dom": "^16.5.2",
"react-json-view": "^1.19.1",
"react-markdown": "^3.6.0",
"react-router-4-compat": "^1.0.8",
"react-sizeme": "^2.5.2",
"redux-persist": "^5.10.0",
"resolve-pathname": "^2.2.0",
"uuid": "^3.3.2",
"word-wrap": "^1.2.3",
"xml2js": "^0.4.17"
"xml2js": "^0.4.19"
},
"devDependencies": {
"@storybook/react": "^3.2.8",
"babel-eslint": "^7.1.1",
"babel-plugin-dva-hmr": "^0.3.2",
"babel-plugin-transform-runtime": "^6.9.0",
"babel-preset-es2016": "^6.24.1",
"babel-preset-es2017": "^6.24.1",
"babel-register": "^6.24.1",
"chai": "^4.0.2",
"cross-env": "^5.0.1",
"ejs-loader": "^0.3.0",
"enzyme": "^2.9.1",
"eslint": "^3.12.2",
"eslint-config-airbnb": "^13.0.0",
"eslint-plugin-import": "^2.2.0",
"eslint-plugin-jsx-a11y": "^2.2.3",
"eslint-plugin-react": "^6.8.0",
"expect": "^1.20.2",
"http-proxy-middleware": "^0.17.4",
"husky": "^0.12.0",
"jsdom": "^11.0.0",
"less": "^2.7.2",
"less-loader": "^4.0.0",
"@babel/core": "^7.1.2",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/plugin-proposal-async-generator-functions": "^7.1.0",
"@babel/plugin-proposal-class-properties": "^7.1.0",
"@babel/plugin-proposal-decorators": "^7.1.0",
"@babel/plugin-proposal-do-expressions": "^7.0.0",
"@babel/plugin-proposal-export-default-from": "^7.0.0",
"@babel/plugin-proposal-export-namespace-from": "^7.0.0",
"@babel/plugin-proposal-function-bind": "^7.0.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/plugin-proposal-optional-catch-binding": "^7.0.0",
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
"@babel/plugin-proposal-pipeline-operator": "^7.0.0",
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-transform-react-constant-elements": "^7.0.0",
"@babel/plugin-transform-react-inline-elements": "^7.0.0",
"@babel/plugin-transform-runtime": "^7.1.0",
"@babel/preset-env": "^7.1.0",
"@babel/preset-react": "^7.0.0",
"@babel/register": "^7.0.0",
"@storybook/addon-actions": "4.0.0",
"@storybook/addon-info": "4.0.0",
"@storybook/addon-links": "4.0.0",
"@storybook/react": "4.0.0",
"@types/webpack": "^4.4.17",
"autoprefixer": "^9.1.5",
"awesome-typescript-loader": "^5.2.1",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^9.0.0",
"babel-loader": "^8.0.4",
"babel-plugin-dev-expression": "^0.2.1",
"babel-plugin-dva-hmr": "^0.4.1",
"babel-plugin-import": "^1.11.0",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-transform-react-remove-prop-types": "^0.4.19",
"cache-loader": "1.2.2",
"case-sensitive-paths-webpack-plugin": "^2.1.2",
"chai": "^4.1.2",
"clean-webpack-plugin": "^0.1.19",
"copy-webpack-plugin": "^4.5.4",
"cross-env": "^5.2.0",
"ejs-loader": "^0.3.1",
"enzyme": "^3.3.0",
"eslint": "^5.5.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.1",
"eslint-plugin-lodash-fp": "^2.1.3",
"eslint-plugin-react": "^7.11.1",
"highlight-loader": "^0.7.2",
"html-webpack-plugin": "^3.2.0",
"http-proxy-middleware": "^0.19.0",
"jest": "^23.6.0",
"jsdom": "^12.0.0",
"less": "^3.8.1",
"less-loader": "^4.1.0",
"lodash-webpack-plugin": "^0.11.5",
"markdown-loader": "^4.0.0",
"mini-css-extract-plugin": "^0.4.2",
"mockjs": "^1.0.1-beta3",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"path": "^0.12.7",
"react-test-renderer": "^15.6.1",
"redbox-react": "^1.3.2",
"roadhog": "^1.2.2"
"postcss-flexbugs-fixes": "^4.1.0",
"postcss-safe-parser": "^4.0.1",
"raw-loader": "^0.5.1",
"react-dev-utils": "^5.0.2",
"react-test-renderer": "^16.5.0",
"redbox-react": "^1.6.0",
"style-loader": "^0.23.1",
"uglifyjs-webpack-plugin": "^1.3.0",
"url-loader": "^1.1.1",
"webpack": "^4.23.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.10",
"webpack-manifest-plugin": "^2.0.4",
"webpack-merge": "^4.1.4"
}
}
module.exports = {
plugins: {
'postcss-flexbugs-fixes': {},
autoprefixer: {
flexbox: 'no-2009',
remove: false,
},
},
};
const proxy = {
target: 'http://192.168.1.22:8080',
changeOrigin: true,
pathRewrite: {
'^/api': '/bm/api',
},
};
const resourceProxy = {
target: 'http://192.168.1.22:8080',
changeOrigin: true,
pathRewrite: {
'^/resource': '/bm/resource',
},
};
module.exports = {
'/api': proxy,
'/resource': resourceProxy,
};
import { makePath } from '../../utils/helper';
const route = (routes) => {
const Wrapper = ({ children }) => {
return children;
return children || null;
};
if (routes.indexRoute) {
Wrapper.route = routes;
} else {
Wrapper.route = {
...routes,
indexRoute: {
onEnter(nextState, replace) {
if (routes && routes.childRoutes && routes.childRoutes.length > 0) {
const index = routes.childRoutes[0];
replace(`${nextState.location.pathname}/${index.path}`);
replace(makePath(nextState.match.url, index.path));
}
},
},
...routes,
};
}
return Wrapper;
};
......
import React from 'react';
import sm from 'react-sizeme';
import hoistStatics from 'hoist-non-react-statics';
import { shallowEqual } from '../../utils/helper';
const SizeMeContext = React.createContext('sizeMe');
export const { Provider, Consumer } = SizeMeContext;
export const provideSize = options => (Component) => {
const WC = class WrappedComponent extends React.Component {
render() {
const { size, forwardedRef, children, ...rest } = this.props;
if (!shallowEqual(this.size, size)) {
this.size = size;
}
return (
<Provider value={this.size}>
<Component ref={forwardedRef} {...rest}>
{children || null}
</Component>
</Provider>
);
}
};
const SizeMeComponent = sm(options)(WC);
const C = React.forwardRef((props, ref) => (
<SizeMeComponent forwardedRef={ref} {...props} />
));
C.displayName = `provideSize(${Component.displayName || Component.name}}`;
return hoistStatics(C, Component);
};
export const provideWidth = provideSize({});
export const provideHeight = provideSize({ monitorHeight: true });
export const withSize = (Component) => {
const C = React.forwardRef((props, ref) => (
<Consumer>
{ size => (
<Component {...props} size={size} ref={ref} />
)}
</Consumer>
));
C.displayName = `withSize(${Component.displayName || Component.name})`;
return hoistStatics(C, Component);
};
import React, { Component } from 'react';
import { connect as dvaConnect } from 'dva';
import _, { startsWith } from 'lodash';
import startsWith from 'lodash/fp/startsWith';
import flow from 'lodash/fp/flow';
import mapKeys from 'lodash/fp/mapKeys';
import pickBy from 'lodash/fp/pickBy';
import { getApp } from '../../data/app';
const connect = (modelCreator, { app, mapStateToProps, mapDispatchToProps, mergeProps, options }) => (Comp) => {
......@@ -12,33 +15,19 @@ const connect = (modelCreator, { app, mapStateToProps, mapDispatchToProps, merge
const { name, model } = modelCreator();
this.name = name;
this.model = model;
}
componentWillMount() {
(app || this.props.app || getApp()).model(this.model);
}
componentWillUnmount() {
(app || this.props.app || getApp()).unmodel(this.model.namespace);
}
render() {
const { children, ...rest } = this.props;
const name = this.name;
const model = this.model;
const mapState = (state) => {
const props = mapStateToProps ? mapStateToProps(state) : {};
props[name] = state[model.namespace];
props[namespaceVar || 'namespace'] = model.namespace;
const pps = mapStateToProps ? mapStateToProps(state) : {};
pps[name] = state[model.namespace];
pps[namespaceVar || 'namespace'] = model.namespace;
if (state.loading) {
props.loading = state.loading;
props.loading.model = props.loading.models[model.namespace];
props.loading.effect = _(props.loading.effects)
.pickBy((v, k) => startsWith(k, `${model.namespace}/`))
.mapKeys((v, k) => k.slice(model.namespace.length + 1))
.value();
pps.loading = state.loading;
pps.loading.model = pps.loading.models[model.namespace];
pps.loading.effect = flow(
pickBy((v, k) => startsWith(`${model.namespace}/`, k)),
mapKeys(k => k.slice(model.namespace.length + 1)),
)(pps.loading.effects);
}
return props;
return pps;
};
const mapDispatch = (dispatch) => {
const extras = mapDispatchToProps ? mapDispatchToProps(dispatch) : {};
......@@ -51,11 +40,22 @@ const connect = (modelCreator, { app, mapStateToProps, mapDispatchToProps, merge
},
};
};
const Output = dvaConnect(mapState, mapDispatch, mergeProps, options)(Comp);
this.Output = dvaConnect(mapState, mapDispatch, mergeProps, options)(Comp);
}
componentWillMount() {
(app || this.props.app || getApp()).model(this.model);
}
componentWillUnmount() {
(app || this.props.app || getApp()).unmodel(this.model.namespace);
}
render() {
const { children, ...rest } = this.props;
return (
<Output {...rest}>
<this.Output {...rest}>
{ children }
</Output>
</this.Output>
);
}
......
import React from 'react';
import PropTypes from 'prop-types';
import { Divider } from 'antd';
import modelCreator from './model';
import connect from '../../hoc/stateful';
import TableEx from '../../../components/table/index';
import TableEx from '../index';
import { push } from '../../../services/route';
import { shallowEqual, arrayJoin } from '../../../utils/helper';
import styles from './index.less';
const createUniqueKey = (columns, key) => {
if (columns.findIndex(v => v.k === key) === -1) {
return key;
} else {
return createUniqueKey(columns, `_${key}`);
}
};
const renderButton = (meta) => {
if (meta.path) {
const onClick = (e) => {
e.preventDefault();
push(meta.path, { ...meta });
};
const onKeyDown = (e) => {
if (e.keyCode === 13) {
e.preventDefault();
push(meta.path, { ...meta });
}
};
// noinspection JSUnresolvedVariable
return (
<a onClick={onClick} onKeyDown={onKeyDown}>
{' '}
{meta.buttonName || 'link'}
{' '}
</a>
);
}
throw new Error(`Unsupported button meta: ${JSON.stringify(meta)}.`);
};
class DsTable extends React.Component {
componentDidMount() {
const { coordinate, params, dispatchLocal } = this.props;
dispatchLocal({ type: 'doInit', payload: { coordinate, params } });
const { coordinate, params, dispatchLocal, current = 1, start, end } = this.props;
dispatchLocal({ type: 'doInit', payload: { coordinate, params, current, start, end } });
}
componentWillReceiveProps(nextProps) {
const { coordinate, params } = nextProps;
const { coordinate, params, current, start, end } = nextProps;
const { dispatchLocal } = this.props;
if (coordinate !== this.props.coordinate || params !== this.props.params) {
dispatchLocal({ type: 'doInit', payload: { coordinate, params } });
if (!shallowEqual(coordinate, this.props.coordinate) || !shallowEqual(params, this.props.params) || current !== this.props.current) {
dispatchLocal({ type: 'doInit', payload: { coordinate, params, current, start, end } });
}
}
render() {
const { dispatchLocal } = this.props;
const { props, columns, current, pageSize, filters, list, num } = this.props.model;
const { dispatchLocal, current: outCurrent } = this.props;
const { parsedMeta, props, columns, current: localCurrent, pageSize, filters, list, num } = this.props.model;
const current = outCurrent || localCurrent;
const tableProps = {
dataSource: list,
...props,
parsedMeta,
columns,
filters: filters.map(filter => filter.filter),
loading: this.props.loading.model,
......@@ -35,8 +72,12 @@ class DsTable extends React.Component {
},
onChange: (pagination, theFilters, sorter) => {
if (current !== pagination.current) {
if (outCurrent) {
this.props.onPageChange(pagination.current);
} else {
dispatchLocal({ type: 'changeCurrentPage', payload: pagination.current });
}
}
if (pageSize !== pagination.pageSize) {
dispatchLocal({ type: 'changePageSize', payload: pagination.pageSize });
}
......@@ -49,6 +90,25 @@ class DsTable extends React.Component {
dispatchLocal({ type: 'doFilter', payload: theFilters });
},
};
const { columnData, rowData } = parsedMeta.global;
if (columnData.buttons || rowData.buttons) {
tableProps.columns = [
...columns,
{
title: '操作',
key: createUniqueKey(tableProps.columns, 'operations'),
fixed: 'right',
width: 150,
render: (text, record, rowIndex, meta, globalMeta) => {
return (
<div style={{ whiteSpace: 'nowrap' }}>
{ arrayJoin(globalMeta.buttons.map(renderButton), <Divider type="vertical" />) }
</div>
);
},
},
];
}
return (
<div className={styles.wrapper}>
<div className={styles.container}>
......@@ -70,6 +130,12 @@ DsTable.propTypes = {
}),
],
).isRequired,
onPageChange: PropTypes.func,
current: PropTypes.number,
};
DsTable.defaultProps = {
onPageChange: () => null,
};
export default connect(modelCreator, {
......
import uuid from 'uuid/v4';
import _ from 'lodash';
import pickBy from 'lodash/pickBy';
import negate from 'lodash/negate';
import isUndefined from 'lodash/isUndefined';
import uniqueId from 'lodash/uniqueId';
import { getKeyName, parseMetas, normMetas, parseQueryResult } from '../../../utils/meta';
import { datasourceApi } from '../../../services/datasource';
const prefix = uuid();
const getKeyName = (meta) => {
const keyProperty = _(meta.properties || [])
.filter(property => !property.skip)
.find(property => property.key);
return keyProperty ? keyProperty.name : undefined;
};
const getSource = (property) => {
if (property.source) {
return (property.source.items || []).map((item) => {
......@@ -25,28 +22,12 @@ const getSource = (property) => {
}
};
const parseMetas = (metas) => {
const ret = {};
if (!metas) {
return ret;
}
_.forEach(metas, (value, key) => {
let finalValue;
try {
finalValue = JSON.parse(value);
} catch (err) {
finalValue = value;
}
_.set(ret, key, finalValue);
});
return _.pickBy(ret, _.negate(_.isUndefined));
};
const makeProps = (meta) => {
// noinspection JSUnresolvedVariable
if (!meta || !meta.metas) {
return {};
}
const props = parseMetas(meta.metas);
const props = normMetas(meta.metas);
if (!props.rowKey) {
props.rowKey = getKeyName(meta);
}
......@@ -61,7 +42,8 @@ const makeColumns = (meta) => {
return (meta.properties || [])
.filter(property => !property.skip)
.map((property) => {
const props = parseMetas(property.metas);
// noinspection JSUnresolvedVariable
const props = normMetas(property.metas);
if (!props.title && props.label) {
props.title = props.label;
}
......@@ -71,16 +53,23 @@ const makeColumns = (meta) => {
if ((props.fixed === true || props.fixed === 'left' || props.fixed === 'right') && props.width === undefined) {
props.width = 150;
}
return _.pickBy({
return pickBy({
...props,
dataIndex: property.name,
key: property.name,
sorter: property.sort,
filterType: property.filterType,
filterEnums: getSource(property),
}, _.negate(_.isUndefined));
}, negate(isUndefined));
})
.filter((c) => {
for (const key of Object.keys(c)) {
if (key.startsWith('meta:')) {
return false;
}
}
return c.visible !== false;
})
.filter(c => c.visible !== false)
.sort((c1, c2) => {
const c1Left = c1.fixed === true || c1.fixed === 'left';
const c1Right = c1.fixed === 'right';
......@@ -110,40 +99,20 @@ const getSort = (columns) => {
} : undefined;
};
const getArrayData = ({ dataType, arrayData, singularData }, meta) => {
if (dataType === 'TABLE') {
const data = (arrayData || []).map(() => ({}));
(meta.properties || [])
.filter(property => !property.skip)
.forEach((property, i) => {
data.forEach((row, j) => {
row[property.name] = arrayData[j][i]; // eslint-disable-line no-param-reassign
});
});
return data;
} else if (dataType === 'PROPERTIES') {
const data = [];
(meta.properties || [])
.filter(property => !property.skip)
.forEach((property) => {
data.push((singularData || {})[property.name]);
});
return [_.toPlainObject(data)];
} else {
throw new Error(`Unsupported data type: ${dataType}`);
}
};
const modelCreator = () => {
const name = 'model';
const namespace = _.uniqueId(prefix);
const namespace = uniqueId(prefix);
const loadMeta = function* loadMeta({ select, call, put }) {
const { coordinate } = yield select(state => state[namespace]);
const { coordinate, params } = yield select(state => state[namespace]);
const api = datasourceApi(coordinate);
const meta = yield call(api.meta);
const meta = yield call(api.queryMeta, { params });
yield put({ type: 'applyMeta', payload: meta });
};
const loadData = function* loadData({ select, put, call }) {
const loadData = function* loadData({ select, put, call, start, end }) {
if (start) {
yield put({ type: start });
}
try {
const { coordinate, params, meta, columns, filters, current, pageSize } = yield select(state => state[namespace]);
const api = datasourceApi(coordinate);
const psz = pageSize;
......@@ -154,8 +123,13 @@ const modelCreator = () => {
const options = { pst, psz, params, filters, sortBys, sortTypes };
const num = yield call(api.count, options);
const dsb = yield call(api.query, options);
const list = getArrayData(dsb, meta);
const list = parseQueryResult(dsb, meta);
yield put({ type: 'applyData', payload: { num, list } });
} finally {
if (end) {
yield put({ type: end });
}
}
};
return {
name,
......@@ -165,6 +139,13 @@ const modelCreator = () => {
coordinate: null,
params: {},
meta: {},
parsedMeta: {
global: {
columnData: {},
rowData: {},
},
properties: {},
},
columns: [],
props: {},
num: 0,
......@@ -222,6 +203,7 @@ const modelCreator = () => {
return {
...state,
meta,
parsedMeta: parseMetas(meta),
props: makeProps(meta),
columns: makeColumns(meta),
};
......@@ -254,12 +236,12 @@ const modelCreator = () => {
},
},
effects: {
*doInit({ payload: { coordinate, params } }, { put, call, select }) {
*doInit({ payload: { coordinate, params, current = 1, start, end } }, { put, call, select }) {
yield put({ type: 'applyTarget', payload: { coordinate, params } });
yield put({ type: 'applyFilters', payload: [] });
yield put({ type: 'applyCurrentPage', payload: 1 });
yield put({ type: 'applyCurrentPage', payload: current });
yield call(loadMeta, { put, call, select });
yield call(loadData, { put, call, select });
yield call(loadData, { put, call, select, start, end });
},
*doFilter({ payload: filters }, { put, call, select }) {
yield put({ type: 'applyFilters', payload: filters });
......
import React from 'react';
class DSTable extends React.PureComponent {
}
export default DSTable;
......@@ -2,6 +2,7 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Table } from 'antd';
import Search from './search';
import { calcPropertyMeta, calcGlobalMeta } from '../../utils/meta';
class TableEx extends Component {
......@@ -36,7 +37,8 @@ class TableEx extends Component {
makeColumns() {
if (!this.props.columns) {
return [];
}
} else {
const { parsedMeta, renderCell } = this.props;
return this.props.columns.map((column, index) => {
const ret = {
...column,
......@@ -44,11 +46,13 @@ class TableEx extends Component {
const state = this.state.columns[index];
if (ret.filterType) {
const onSearch = (filter) => {
const newColumns = [...this.state.columns];
this.setState((preState) => {
const newColumns = [...preState.columns];
newColumns[index].filter = filter;
newColumns[index].filtered = !!filter;
newColumns[index].filterDropdownVisible = false;
this.setState({ columns: newColumns }, () => {
return { columns: newColumns };
}, () => {
this.props.onFilter(this.state.columns.map(c => ({
key: c.key,
filter: c.filter,
......@@ -60,15 +64,26 @@ class TableEx extends Component {
);
ret.filterDropdownVisible = state.filterDropdownVisible;
ret.onFilterDropdownVisibleChange = (visible) => {
const newColumns = [...this.state.columns];
this.setState((preState) => {
const newColumns = [...preState.columns];
newColumns[index].filterDropdownVisible = visible;
this.setState({ columns: newColumns });
return { columns: newColumns };
});
};
ret.filterIcon = Search.getIcon(ret.filterType, state.filtered);
}
const orgRender = ret.render;
ret.render = (text, record, rowIndex) => {
if (column.dataIndex !== undefined && column.dataIndex !== null) {
return (orgRender || renderCell)(text, record, rowIndex, calcPropertyMeta(column.dataIndex, parsedMeta, record), calcGlobalMeta(parsedMeta, record), column.dataIndex);
} else {
return (orgRender || renderCell)(text, record, rowIndex, null, calcGlobalMeta(parsedMeta, record), column.dataIndex);
}
};
return ret;
}, this);
}
}
render() {
return (
<Table {...this.props} columns={this.makeColumns()} />
......@@ -80,10 +95,22 @@ const funcVoid = () => {};
TableEx.propTypes = {
onFilter: PropTypes.func,
parsedMeta: PropTypes.shape({
global: PropTypes.shape({
columnData: PropTypes.object,
rowData: PropTypes.objectOf(PropTypes.string),
}),
properties: PropTypes.objectOf(PropTypes.shape({
columnData: PropTypes.object,
rowData: PropTypes.objectOf(PropTypes.string),
})),
}),
renderCell: PropTypes.func,
};
TableEx.defaultProps = {
onFilter: funcVoid,
// (cellData, rowData, rowIndex, meta, globalMeta, propertyName) => string|React node
renderCell: data => `${data || ''}`,
};
export default TableEx;
import React, { Component } from 'react';
import _ from 'lodash';
import castArray from 'lodash/castArray';
import noop from 'lodash/noop';
import { Table, Button, Form } from 'antd';
class TableInput extends Component {
......@@ -23,11 +24,11 @@ class TableInput extends Component {
onOk() {
const { value, items, form, onChange } = this.props;
const row = {};
_.castArray(items).forEach((item) => {
castArray(items).forEach((item) => {
row[item.name] = form.getFieldValue(item.name);
});
if (this.state.operation === 'add') {
(onChange || _.noop)([...(value || []), row]);
(onChange || noop)([...(value || []), row]);
}
this.setState({
editing: false,
......@@ -36,7 +37,7 @@ class TableInput extends Component {
}
makeColumns() {
const { items } = this.props;
return _.castArray(items)
return castArray(items)
.map((item) => {
return {
...item,
......@@ -48,7 +49,7 @@ class TableInput extends Component {
}
makeDataSource() {
const { value } = this.props;
return _.castArray(value || [])
return castArray(value || [])
.map((row, idx) => ({
...row,
__key__: idx,
......@@ -58,8 +59,9 @@ class TableInput extends Component {
render() {
const { items, form, children } = this.props;
if (this.state.editing) {
const fields = _.castArray(children)
.map((child, i) => {
const fields = castArray(children)
.map(
(child, i) => {
const item = items[i];
return (
<Form.Item {...item}>
......
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Checkbox, Button } from 'antd';
import { isString } from 'lodash';
import isString from 'lodash/isString';
import styles from './enum.less';
class EnumSearch extends Component {
static getValue(filterEnum) {
if (isString(filterEnum)) {
return filterEnum;
......@@ -44,9 +45,9 @@ class EnumSearch extends Component {
}
}
reset() {
this.setState({
checked: this.state.checked.map(() => false),
});
this.setState(preState => ({
checked: preState.checked.map(() => false),
}));
}
render() {
......@@ -56,9 +57,11 @@ class EnumSearch extends Component {
{
filterEnums.map((filterEnum, index) => {
const onChange = (e) => {
const checked = [...this.state.checked];
this.setState((preState) => {
const checked = [...preState.checked];
checked[index] = e.target.checked;
this.setState({ checked });
return { checked };
});
};
return (
<span key={index}>
......
import persistStore from 'redux-persist/lib/persistStore';
import { histories } from '../utils/auth';
const data = {
......@@ -7,11 +8,18 @@ const data = {
export async function initApp(app) {
data.app = app;
await histories.init();
await histories.createHistory('domain', 10);
await histories.createHistory('module', 50);
await histories.createHistory('domain', null, 10);
await histories.createHistory('module', null, 50);
return data.app;
}
export function getApp() {
return data.app;
}
export const getStore = () => {
return data.app._store; // eslint-disable-line no-underscore-dangle
};
// eslint-disable-next-line no-underscore-dangle
export const createPersistor = theApp => persistStore((theApp || getApp())._store);
import { flatMap } from 'lodash';
import flatMap from 'lodash/flatMap';
import { fetchMenus, fetchModuleInfos, fetchModuleLayout } from '../services/modules';
const data = {
......@@ -46,7 +46,7 @@ const combineMenus = (menus1, menus2) => {
export const getMenus = async () => {
if (!data.flags.menus) {
const context = require.context('../register/modules', true, /^.*\.(js|jsx|json)/);
const staticMenus = flatMap(context.keys(), context);
const staticMenus = flatMap(context.keys(), k => context(k).default);
const dynamicMenus = await fetchMenus();
data.menus = combineMenus(staticMenus, dynamicMenus);
data.flags.menus = true;
......
@charset "utf-8";
* {
/** {
margin: 0;
padding: 0;
}
}*/
html {
color: #000;
background: #FFF;
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
}
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form,
/*body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, form,
fieldset, input, textarea, p, blockquote, th, td {
margin: 0;
padding: 0;
......@@ -61,11 +67,17 @@ li {
input, img {
border: none;
}
}*/
:global(#root) {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
.globalSpin {
width: 100%;
margin: 40px 0 !important;
}
......@@ -3,9 +3,9 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Dva Demo</title>
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="https://cdn.bootcss.com/babel-polyfill/6.26.0/polyfill.min.js"></script>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title><%= htmlWebpackPlugin.options.title %></title>
<!--<script src="https://cdn.bootcss.com/babel-polyfill/7.0.0-beta.3/polyfill.min.js"></script>-->
<!--<link rel="stylesheet" href="/index.css" />-->
</head>
<body>
......
/* eslint-disable no-underscore-dangle */
import dva from 'dva';
import createLoading from 'dva-loading';
import moment from 'moment';
import { history } from './services/route';
import persistReducer from 'redux-persist/lib/persistReducer';
import storage from 'redux-persist/lib/storage';
import { getHistory } from './services/route';
import { initApp } from './data/app';
import appModel from './models/app';
import routerConfig from './router';
import { processError } from './utils/error';
import config from './utils/config';
import './index.css';
moment.locale('zh-cn');
const persistConfig = {
key: 'root',
debug: process.env.NODE_ENV === 'development',
storage,
};
// 1. Initialize
const app = dva({
history,
history: getHistory({
basename: config.basename,
}),
onReducer: reducer => persistReducer(persistConfig, reducer),
onError(error) {
// eslint-disable-next-line no-console
// eslint-disable-next-line no-console
processError(error);
},
});
......@@ -34,10 +47,3 @@ app.router(routerConfig);
// 5. Start
initApp(app).then(theApp => theApp.start('#root'));
export default app;
export const getStore = () => {
// eslint-disable-next-line no-underscore-dangle
return app._store;
};
import Mock from 'mockjs';
import moment from 'moment';
import _ from 'lodash';
import flatMap from 'lodash/flatMap';
const defaultDateFormat = 'YYYY-MM-DD';
const defaultTimeFormat = 'HH:mm:ss';
......@@ -16,7 +16,7 @@ export function getTasks() {
id: idx,
name: `流程${idx}`,
}));
const tasks = _.flatMap(progresses, (progress) => {
const tasks = flatMap(progresses, (progress) => {
const s = Random.natural(0, 100);
const e = Random.natural(start + 5, start + 20);
return Random.range(s, e).map((idx) => {
......
......@@ -2,34 +2,34 @@ import { routerRedux } from 'dva/router';
import { authorize, login, userInfo } from '../services/login';
import { validate as passValidate } from '../services/login/password';
import { requestCode, validate as ucaValidate } from '../services/login/uca';
import { fullPath, encrypt } from '../utils/helper';
import { encrypt } from '../utils/helper';
import { setToken, setUser, setDomain, histories } from '../utils/auth';
import { switchDomain, currentDomain } from '../services/domain';
import { errors } from '../utils/error';
import config from '../utils/config';
import { getStore } from '../index';
import { getStore } from '../data/app';
const successAuthed = async (tokenId, userName, remember) => {
await histories.pushHistory('userName', remember ? userName : '');
await histories.pushHistory('userName', null, remember ? userName : '');
await setToken(tokenId);
const uInfo = await userInfo();
await setUser(uInfo.id, uInfo.name);
const path = await histories.getLatest('domain');
const path = await histories.getLatest('domain', uInfo.id);
if (!path) {
getStore().dispatch(routerRedux.push(fullPath('/domain')));
getStore().dispatch(routerRedux.push('/domain'));
} else {
await switchDomain(path);
const domain = await currentDomain();
if (domain) {
await setDomain(domain.name, path);
const latest = await histories.getLatest('module');
const latest = await histories.getLatest('module', uInfo.id);
if (latest && config.fastNavigationPage) {
getStore().dispatch(routerRedux.push(fullPath('/fastNav')));
getStore().dispatch(routerRedux.push('/fastNav'));
} else {
getStore().dispatch(routerRedux.push(fullPath('/main')));
getStore().dispatch(routerRedux.push('/main'));
}
} else {
getStore().dispatch(routerRedux.push(fullPath('/domain')));
getStore().dispatch(routerRedux.push('/domain'));
}
}
};
......@@ -91,7 +91,7 @@ export default {
yield put({ type: 'setStatus', payload: 'login' });
yield put({ type: 'setUCACode', payload: '' });
yield put({ type: 'setAuthRequires', payload: [] });
const userName = yield call(histories.getLatest, 'userName');
const userName = yield call(histories.getLatest, 'userName', null);
yield put({ type: 'setUserName', payload: userName || '' });
},
*login({ payload: userName }, { call, put }) {
......
import { routerRedux } from 'dva/router';
import { isString } from 'lodash';
import isString from 'lodash/isString';
import { logout } from '../../services/main';
import { getMenus, getModuleInfo } from '../../data/modules';
import { fullPath } from '../../utils/helper';
import { delToken, getUser, delUser } from '../../utils/auth';
const createMenu = async (configure) => {
......@@ -86,7 +85,7 @@ export default {
*fetchUser(ignored, { put, call }) {
const user = yield call(getUser);
if (!user) {
yield put(routerRedux.push(fullPath('/login')));
yield put(routerRedux.push('/login'));
}
yield put({ type: 'queryUserSuccess', payload: user.name });
},
......@@ -99,7 +98,7 @@ export default {
yield call(logout);
yield call(delToken);
yield call(delUser);
yield put(routerRedux.push(fullPath('/login')));
yield put(routerRedux.push('/login'));
},
},
......
export default {
namespace: 'apiDoc',
state: {},
reducers: {},
effects: {},
subscriptions: {},
};
import moment from 'moment';
import config from '../../../../utils/config';
import { countTasks, fetchTasks } from '../../../../services/bpm';
export default {
export default (info, layout) => ({
namespace: 'task',
state: {
num: 0,
list: [],
},
reducers: {
queryCountSuccess(state, { payload: num }) {
return {
...state,
num,
};
},
queryTasksSuccess(state, { payload: list }) {
return {
...state,
list,
};
},
},
effects: {
*fetchTasks({ payload: { pst, psz, filters } }, { put, call }) {
const num = Number.parseInt(yield call(countTasks, filters), 10);
yield put({ type: 'queryCountSuccess', payload: num });
const tasks = yield call(fetchTasks, { pst, psz, filters });
yield put({
type: 'queryTasksSuccess',
payload: tasks.map((task) => {
return {
key: `${task.pId}-${task.nId}`,
pName: task.pName,
nName: task.nName,
state: task.state,
date: moment(task.date, config.defaultDateTimeFormat),
deadline: moment(task.deadline, config.defaultDateTimeFormat),
};
}),
});
},
coordinate: layout.option.coordinate,
},
reducers: {},
effects: {},
subscriptions: {},
};
});
/* eslint-disable no-extend-native */
import '@babel/polyfill';
String.prototype.trimLeft = String.prototype.trimLeft ? String.prototype.trimLeft : function trimLeft() {
const trimmed = this.trim();
const indexOfWord = this.indexOf(trimmed);
return this.slice(indexOfWord, this.length);
};
String.prototype.trimStart = String.prototype.trimStart ? String.prototype.trimStart : String.prototype.trimLeft;
String.prototype.trimRight = String.prototype.trimRight ? String.prototype.trimRight : function trimRight() {
const trimmed = this.trim();
const indexOfWord = this.indexOf(trimmed);
return this.slice(0, indexOfWord + trimmed.length);
};
String.prototype.trimEnd = String.prototype.trimEnd ? String.prototype.trimEnd : String.prototype.trimRight;
export default [/* {
/* eslint-disable */
export default [/*{
name: 'test-menu',
showName: '测试',
modules: [{
name: 'test-module',
showName: '测试',
layout: {
route: 'test',
route: 'api-doc',
},
}],
}*/];
/* eslint-disable no-underscore-dangle */
import React from 'react';
import PropTypes from 'prop-types';
import { Router } from 'dva/router';
import _ from 'lodash';
import { Router4Compat as Router } from 'react-router-4-compat';
import { LocaleProvider, Spin } from 'antd';
import zhCN from 'antd/lib/locale-provider/zh_CN';
import { PersistGate } from 'redux-persist/integration/react';
import isString from 'lodash/isString';
import config from './utils/config';
import { isAuthed, histories } from './utils/auth';
import { fullPath, makePromise0 } from './utils/helper';
import { getUser, isAuthed, histories } from './utils/auth';
import { processError } from './utils/error';
import App from './routes/app';
import { getMenus, getModuleInfo, getModuleLayout } from './data/modules';
import { bindModel } from './utils/model';
import Monk from './routes/main/monk';
import { createPersistor } from './data/app';
const { contextPath } = config;
import styles from './index.css';
const Loading = <Spin size="large" className={styles.globalSpin} />;
const registerModel = (app, model) => {
// eslint-disable-next-line no-underscore-dangle
// noinspection JSUnresolvedVariable
if (!(app._models.filter(m => m.namespace === model.namespace).length === 1)) {
app.model(model);
}
......@@ -21,7 +28,7 @@ const registerModel = (app, model) => {
const maybeLogin = async (replace) => {
if (!(await isAuthed())) {
return replace(fullPath('/login'));
return replace('/login');
}
};
......@@ -61,6 +68,29 @@ const createRoute = async (app, group, basePath) => {
};
};
const moduleLeaveHook = (app, module) => {
const models = (app._models || []).filter(m => m.namespace.startsWith(`${module.name}/`));
const store = app._store;
models.forEach((m) => {
const { reducers, effects } = m;
if ((reducers && reducers['@@exit']) || (effects && effects['@@exit'])) {
store.dispatch({ type: `${m.namespace}/@@exit`, payload: module });
}
store.dispatch({ type: `${m.namespace}/@@reset` });
});
};
const moduleEnterHook = (app, uid, module) => {
const models = (app._models || []).filter(m => m.namespace.startsWith(`${module.name}/`));
const store = app._store;
models.forEach((m) => {
const { reducers, effects } = m;
if ((reducers && reducers['@@enter']) || (effects && effects['@@enter'])) {
store.dispatch({ type: `${m.namespace}/@@enter`, payload: module });
}
});
histories.pushHistory('module', uid, module);
};
const createRoutes = async (app, modules, groups, basePath) => {
const routes = [];
......@@ -68,27 +98,30 @@ const createRoutes = async (app, modules, groups, basePath) => {
for (const module of modules) {
let info;
let layout;
if (_.isString(module)) {
if (isString(module)) {
info = await getModuleInfo(module);
layout = await getModuleLayout(module);
} else {
info = module;
layout = module.layout;
}
const { name, showName, icon, description } = info;
const { name, showName } = info;
const route = {
path: name,
fullPath: combinePath(basePath, name),
name: showName,
};
if (layout.route) {
const modelBundle = await makePromise0(require('bundle!./models/main/modules/' + layout.route + '/index.js')); // eslint-disable-line import/no-dynamic-require, prefer-template, global-require
registerModel(app, modelBundle);
const routeBundle = await makePromise0(require('bundle!./routes/main/modules/' + layout.route + '/index.js')); // eslint-disable-line import/no-dynamic-require, prefer-template, global-require
if (layout.route || layout.template) {
let routeBundle = await import(`./routes/main/modules/${layout.route || layout.template}`);
routeBundle = routeBundle.default || routeBundle;
const binder = bindModel(app, info, layout);
routeBundle = routeBundle(binder, info, layout);
route.component = routeBundle;
if (routeBundle.route) {
for (const key in routeBundle.route) {
// noinspection JSUnfilteredForInLoop
if ({}.hasOwnProperty.call(routeBundle.route, key)) {
// noinspection JSUnfilteredForInLoop
route[key] = routeBundle.route[key];
}
}
......@@ -96,17 +129,28 @@ const createRoutes = async (app, modules, groups, basePath) => {
} else {
route.component = Monk;
}
const infoEx = {
...info,
path: route.fullPath,
};
const { onLeave } = route;
if (onLeave) {
route.onLeave = (preState) => {
moduleLeaveHook(app, infoEx);
onLeave(preState);
};
} else {
route.onLeave = () => {
moduleLeaveHook(app, infoEx);
};
}
if (route.onEnter) {
const onEnter = route.onEnter;
route.onEnter = (nextState, replace, cb) => {
if (nextState && nextState.location && nextState.location.pathname === route.fullPath) {
histories.pushHistory('module', {
name,
showName,
icon,
description,
path: route.fullPath,
}).then(() => new Promise((resolve, reject) => {
getUser()
.then(u => u.id)
.then(uid => moduleEnterHook(app, uid, infoEx))
.then(() => new Promise((resolve, reject) => {
onEnter(nextState, replace, (err, res) => {
if (err) {
reject(err);
......@@ -114,24 +158,17 @@ const createRoutes = async (app, modules, groups, basePath) => {
resolve(res);
}
});
})).then(() => cb()).catch(err => cb(err));
} else {
return onEnter(nextState, replace, cb);
}
}))
.then(() => cb())
.catch(err => cb(err));
};
} else {
route.onEnter = (nextState, replace, cb) => {
if (nextState && nextState.location && nextState.location.pathname === route.fullPath) {
histories.pushHistory('module', {
name,
showName,
icon,
description,
path: route.fullPath,
}).then(() => cb()).catch(err => cb(err));
} else {
cb();
}
getUser()
.then(u => u.id)
.then(uid => moduleEnterHook(app, uid, infoEx))
.then(() => cb())
.catch(err => cb(err));
};
}
routes.push(route);
......@@ -148,18 +185,22 @@ const createRoutes = async (app, modules, groups, basePath) => {
function RouterConfig({ history, app }) {
const routes = [
{
path: `${contextPath}/`,
path: '/',
component: App,
indexRoute: {
onEnter: (nextState, replace, cb) => {
histories.getLatest('module').then((latest) => {
getUser()
.then(u => u && u.id)
.then(uid => histories.getLatest('module', uid))
.then((latest) => {
if (latest && config.fastNavigationPage) {
replace(`${contextPath}/fastNav`);
replace('/fastNav');
} else {
replace(`${contextPath}/main`);
replace('/main');
}
cb();
}).catch((err) => {
})
.catch((err) => {
cb(err);
});
},
......@@ -168,10 +209,13 @@ function RouterConfig({ history, app }) {
{
path: 'login',
getComponent(ignored, cb) {
require.ensure([], (require) => {
registerModel(app, require('./models/login'));
cb(null, require('./routes/login'));
}, 'login');
Promise.all([
import('./models/login'),
import('./routes/login'),
]).then(([model, route]) => {
registerModel(app, model.default);
cb(null, route.default);
});
},
},
{
......@@ -180,15 +224,18 @@ function RouterConfig({ history, app }) {
authenticated(replace).then(() => cb()).catch(err => cb(err));
},
getComponent(ignored, cb) {
require.ensure([], (require) => {
registerModel(app, require('./models/main'));
cb(null, require('./routes/main'));
}, 'main');
Promise.all([
import('./models/main'),
import('./routes/main'),
]).then(([model, route]) => {
registerModel(app, model.default);
cb(null, route.default);
});
},
getChildRoutes: (nextState, cb) => {
getMenus()
.then((menus) => {
createRoutes(app, [], menus, `${contextPath}/main`)
createRoutes(app, [], menus, '/main')
.then((result) => {
cb(null, result);
})
......@@ -211,21 +258,28 @@ function RouterConfig({ history, app }) {
authenticated(replace).then(() => cb()).catch(err => cb(err));
},
getComponent(ignored, cb) {
require.ensure([], (require) => {
registerModel(app, require('./models/main/modules/' + config.fastNavigationPage)); // eslint-disable-line import/no-dynamic-require, prefer-template
cb(null, require('./routes/main/modules/' + config.fastNavigationPage)); // eslint-disable-line import/no-dynamic-require, prefer-template
}, 'fastNav');
Promise.all([
import(`./models/main/modules/${config.fastNavigationPage}`),
import(`./routes/main/modules/${config.fastNavigationPage}`),
]).then(([model, route]) => {
registerModel(app, model.default);
cb(null, route.default);
});
},
});
}
return (
<PersistGate persistor={createPersistor(app)} loading={Loading}>
<LocaleProvider locale={zhCN}>
<Router history={history} routes={routes} onError={processError} />
</LocaleProvider>
</PersistGate>
);
}
RouterConfig.propTypes = {
history: PropTypes.object,
app: PropTypes.object,
history: PropTypes.object.isRequired,
app: PropTypes.object.isRequired,
};
export default RouterConfig;
......@@ -5,12 +5,12 @@ import styles from './app.less';
const App = ({ children }) => {
return (
<div className={styles.app}>{children}</div>
<div className={styles.app}>{children || null}</div>
);
};
App.propTypes = {
children: PropTypes.element.isRequired,
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
};
export default connect()(App);
......@@ -115,7 +115,7 @@ class LoginForm extends React.Component {
render() {
const { getFieldDecorator } = this.props.form;
const loading = this.props.loading;
const { loading } = this.props;
return (
<div className={styles.canvas}>
<div className={styles.container}>
......
/* eslint-disable no-param-reassign */
import React from 'react';
import PropTypes from 'prop-types';
import { Menu, Breadcrumb, Icon } from 'antd';
import { connect } from 'dva';
import { Link } from 'dva/router';
import { fullPath } from '../../utils/helper';
import styles from './header.less';
import { makePath } from '../../utils/helper';
const MenuItem = Menu.Item;
const SubMenu = Menu.SubMenu;
const normRoutes = (routes) => {
if (routes && routes.length > 0) {
const out = [];
let base = '/';
for (let i = 0; i < routes.length; ++i) {
const route = routes[i];
if (route.path) {
base = makePath(base, route.path, false);
if (out.length === 0 || out[out.length - 1].path !== base) {
out.push({ ...route, path: base });
}
}
}
return out;
}
return routes;
};
// const makeRelativePath = (base, path) => {
// if (path === undefined || path === null || !base || !base.startsWith('/')) {
// throw new Error(`Invalid arguments: '${base}' / '${path}'`);
// }
// if (!path.startsWith('/')) {
// return path;
// }
// if (!base.endsWith('/')) {
// base = `${base}/`;
// }
// if (base.length > path.length) {
// if (base === `${path}/`) {
// return '';
// }
// throw new Error(`Invalid arguments: '${base}' / '${path}'`);
// }
// if (path.startsWith(base)) {
// return path.slice(base.length);
// }
// throw new Error(`Invalid arguments: '${base}' / '${path}'`);
// };
//
// const normRoutes2 = (routes) => {
// if (routes && routes.length > 0) {
// const out = [];
// let base = '/';
// for (let i = 0; i < routes.length; ++i) {
// const route = routes[i];
// const path = route.path || '';
// const newBase = makePath(base, path);
// if (out.length === 0 || out[out.length - 1].path !== newBase) {
// if (path === '/') {
// out.push(route);
// } else {
// out.push({ ...route, path: makeRelativePath(base, path) });
// }
// }
// base = newBase;
// }
// return out;
// }
// return routes;
// };
class HeaderPane extends React.Component {
componentDidMount() {
......@@ -30,9 +92,16 @@ class HeaderPane extends React.Component {
dispatch({ type: 'main/logout' });
}
};
const joinPath = (paths) => {
if (paths && paths.length > 0) {
// eslint-disable-next-line
return `/${paths[paths.length - 1]}`;
}
return '/';
};
const breadsProps = {
className: styles.breads,
routes,
routes: nRoutes,
params,
itemRender(route, _params, _routes, paths) {
if (!paths || !paths.length) {
......@@ -42,7 +111,7 @@ class HeaderPane extends React.Component {
return <Icon type="home" />;
}
const bread = route.name ? route.name : route.path;
return <Link to={fullPath(`/${paths.join('/')}`)}>{ bread }</Link>;
return <Link to={joinPath(paths)}>{ bread }</Link>;
},
};
const menuProps = {
......@@ -56,13 +125,13 @@ class HeaderPane extends React.Component {
<div className={styles.board}>
<Breadcrumb {...breadsProps} />
<Menu {...menuProps}>
<SubMenu title={userTitle} key="user" >
<MenuItem key="logout">
<SubMenu title={userTitle} key="user">
<Menu.Item key="logout">
<span>
<Icon type="logout" />
登出
</span>
</MenuItem>
</Menu.Item>
</SubMenu>
</Menu>
</div>
......
......@@ -8,6 +8,10 @@
float: left;
margin-left: 16px;
}
:global .ant-breadcrumb {
line-height: inherit;
font-size: 12px;
}
.menu {
float: right;
......
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'dva';
import { withRouter } from 'dva/router';
import { withRouter4Compat as withRouter } from 'react-router-4-compat';
import { Layout } from 'antd';
import { provideHeight } from '../../components/hoc/size';
import SiderLogo from '../../components/layout/sider/SiderLogo';
import SiderMenu from '../../components/layout/sider/SiderMenu';
import HeaderPane from './header';
......@@ -10,6 +11,7 @@ import { thisPush } from '../../services/route';
import styles from './index.less';
const { Sider, Header, Content, Footer } = Layout;
const ContentWithHeight = provideHeight(Content);
class Main extends React.Component {
......@@ -61,9 +63,9 @@ class Main extends React.Component {
<Header>
<HeaderPane {...headerProps} />
</Header>
<Content>
{ children }
</Content>
<ContentWithHeight>
{ children || null }
</ContentWithHeight>
<Footer>
<span className={styles.company}>®上海铂蓝信息科技有限公司</span>
</Footer>
......@@ -75,8 +77,8 @@ class Main extends React.Component {
}
Main.propTypes = {
children: PropTypes.element,
main: PropTypes.object.isRequired,
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
main: PropTypes.object,
};
export default connect(({ main }) => ({ main }))(withRouter(Main));
# 登录与认证
登录分为2个步骤。
第一步通过用户名或其他用户标识符确定用户的存在性,
并获得一个未认证的token(令牌)。
同时返回的还有该用户需要经过哪些认证方式才能真正登录。
第二步使用上一步获得的令牌,并根据给予的验证需求,使用对应的方式予以认证。
所有验证需求都验证通过后,才正式登录成功,并且token变为有效。
之后所有的api调用都会用到此token。
## 获取一个未验证token
- **URL**:
/api/auth/login
- **Method**:
POST
- **Accept**: application/json
```
{
type: string,
data: string,
tokenInfo: {
productId: string,
deviceId: string
},
authRequest: {
type: string,
parameters: Map
}
}
```
- *type* - **string** 登录方式, 可选值为`userName``email`,分别表示用户名登录和邮箱登录。
- *data* - **string** 登录方式对应的登录数据, `userName`对应的登录数据即为用户名,`email`对应的登录数据即为邮箱地址
- *tokenInfo* - **object** 登录必备的信息,为权限验证所需。
- *productId* - **string** 产品ID,表示要登录的产品。
- *deviceId* - **string** 设备ID,一条可以用来唯一标识客户端的字符串,生成规则不限,只要保证唯一性和不变性即可。
- *authRequest* - **object** 验证请求,为了简化接口,登录的同时也可以一起进行验证。
前提是事先知道对应用户需要用哪种方式进行验证。因为用户所需的验证方式是后台可配置,
不是一成不变的,所以不建议登录验证同时进行的api调用方式。
- *type* - **string** 验证方式。可选值为`password`,表示密码登录
- *parameters* - **object** 验证参数。`password`对应的验证参数为
`{ cipher: string }`,表示用户密码。
- **Content-Type**:
application/json
- *tokenId* - **string** 未加密的原始token字符串,该token在服务器存在内存中,
若超过30分钟没有使用该token,该token将失效。
- *authResponse* - **object** 验证响应,请求体中包含`authRequest`字段时,响应体中才会有此字段。
- *type* - **string** 验证类型,与请求体中`authRequest.type`值相同。
- *status* - **string** 验证状态,可能值为:`authed`,`rejected`,`skipped`等。
`authed`表示验证通过,`rejected`表示验证未通过,`skipped`表示不需要此步验证。
以上3种可能值为所有验证方式共有。其他可能值依赖于各自的验证方式。
- *data* - **any** 验证响应数据。其含义依赖于验证方式。
- *remainedAuthRequirements* - **object** 剩余的验证需求。
- *requirements* - **[object]** 若干个验证需求,逻辑关系为和。
若为空数组,表示所有验证需求均已满足,即全部验证成功。
- *authTypes*: - **[string]** 若干个验证方式,逻辑关系为或,
只要其中一个验证方式验证成功,该验证条件即满足。
- *remainedAuthRequirements* - **object**`authResponse.remainedAuthRequirements`
不同之处在于请求体中不需要包含`authRequest`,该字段也存在。
```
{
tokenId: string,
authResponse: {
type: string,
status: string,
data: any,
remainedAuthRequirements: {
requirements: [{
authTypes: [string]
}]
}
},
remainedAuthRequirements: {
requirements: [{
authTypes: [string]
}]
}
}
```
- **note**:
获取后的token,不能直接使用,需要使用公钥进行非对称加密。加密方式如下:
1. 直接使用token,或包装成令牌对象:
```
{
tkId: string, //令牌
ett: long, //令牌发出时间,从1970/1/1 UTC至今的毫秒数
life: long //令牌对象有效期,单位毫秒
}
```
2. 将token字符串本身或包装成的令牌对象的json字符串表示转为utf-8字节数组。
3. 使用给定公钥,以**RSA/None/PKCS1Padding**方式进行加密。然后对加密结果进行base64urlsafing方式转码
(先进行普通的base64转码,然后去除尾部'=',然后用'-'替换所有'+',用'_'替换所有'/')
- **公钥**:
`-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+/Rs6dYmdtETjHCZq4LF3QjLM/DocRAXrqtMULZf+579dAn+CiM8noLplZT/DRwvfK822eq8sypH+a4NqP7942pPVjOudVvKfiJvmm2TOQHvQ7vi3iyZVdlsxX72JNFo1Ocqwj48aIC/OJ4bMf/VyCKrmKrU2iXND+I4BN8cfhwIDAQAB-----END PUBLIC KEY-----`
## 验证token
- **URL**:
/api/auth/authorize
- **Method**:
POST
- **Accept**:
application/json
```
{
tkId: string,
request: {
type: string, //验证方式
parameters: Map //验证参数
}
}
```
- *tkId* - **string** 加密后的令牌
- *request* -**object** 验证请求
- *type* - **string** 验证方式。可选值为`password`,表示密码登录
- *parameters* - **object** 验证参数。`password`对应的验证参数为
`{ cipher: string }`,表示用户密码。
- **Content-Type**:
application/json
```
{
type: string,
status: string,
data: any,
remainedAuthRequirements: {
requirements: [{
authTypes: [string]
}]
}
}
```
- *type* - **string** 验证类型,与请求体中`authRequest.type`值相同。
- *status* - **string** 验证状态,可能值为:`authed`,`rejected`,`skipped`等。
`authed`表示验证通过,`rejected`表示验证未通过,`skipped`表示不需要此步验证。
以上3种可能值为所有验证方式共有。其他可能值依赖于各自的验证方式。
- *data* - **any** 验证响应数据。其含义依赖于验证方式。
- *remainedAuthRequirements* - **[object]** 剩余的验证需求。
- *requirements* - **[object]** 若干个验证需求,逻辑关系为和。
若为空数组,表示所有验证需求均已满足,即全部验证成功。
- *authTypes* - **[string]** 若干个验证方式,逻辑关系为或,
只要其中一个验证方式验证成功,该验证条件即满足。
## 登出
- **URL**:
/api/auth/logout
- **Method**:
POST
- **Accept**:
application/json
- `empty` - 只包含前置说明中提到的token,即`{ token: string }`, 之后不再重复说明。
- **Content-Type**:
application/json
`null` - 并不是真正的空,仅仅是如前置说明中提到那样,`data`字段为`null`, 之后不再重复说明。
import React from 'react';
import PropTypes from 'prop-types';
import cs from 'classnames';
import hljs from 'highlight.js/lib/highlight';
import langJson from 'highlight.js/lib/languages/json';
import langJavascript from 'highlight.js/lib/languages/javascript';
import 'highlight.js/styles/github.css';
hljs.registerLanguage('json', langJson);
hljs.registerLanguage('javascript', langJavascript);
class Code extends React.Component {
componentDidMount() {
this.highlightCode();
}
componentDidUpdate() {
this.highlightCode();
}
setRef = (el) => {
this.codeEl = el;
};
highlightCode = () => {
hljs.highlightBlock(this.codeEl);
};
render() {
return (
<pre>
<code
ref={this.setRef}
className={cs({
[`language-${this.props.language}`]: !!this.props.language,
})}
>
{ this.props.value }
</code>
</pre>
);
}
}
Code.propTypes = {
value: PropTypes.string.isRequired,
language: PropTypes.string,
};
Code.defaultProps = {
language: '',
};
export default Code;
# 作用域
用户永远处于某一作用域下。作用域从高到低,
分别是全市,区,项目,基地。每一个作用域都有自己的唯一编号。
任意一个作用域可以用作用域路径来唯一标识。
作用域路径即将此作用域从高到低的各个作用域的编号拼接成一个路径地址。
特别的,全市的作用域路径为`/`
举个例子。假设黄浦区的作用域编号为HP,
黄浦区下某项目的作用域编号为HP01,
该项目下某基地的作用域编号为HP011,
那么黄浦区的作用域路径为`/HP`
项目HP01的作用域路径为`/HP/HP01`
基地HP011的作用域路径为`/HP/HP01/HP011`.
绝大多数接口都是受限于当前作用域的。
处于错误的作用域,调用相同名称的接口,
即使参数都一样,也往往产生错误的结果。
所以登录验证后第一件事就是切换到正确的作用域。
除了使用接口切换当前作用域,就如前置说明中提及的一样,
大多数接口可接受一个`dmPath`参数,此参数是可选的,用于在执行当前接口期间临时指定一个作用域。
并不会改变当前作用域。这个指定的作用域路径可以是绝对路径,也可以是相对路径。
若路径以`/`开头,则认为是绝对路径,直接作为输入的作用域路径使用。当然会事先检查用户是否有相关作用域权限。
若路径不以`/`开头,则认为是相对路径,会拼接上当前作用域路径后再作为输入路径使用。所以此参数的前后不要带上空白字符,以免引起误会。
## 切换作用域
当前作用域是临时地保存在服务端的,每次重新登录后,之前设置的作用域都将失效,必须重新设置。
- **URL**:
/api/domain/user/path
- **Method**:
POST
- **Accept**:
application/json
- *dmPath* - **string** 作用域路径
- **Content-Type**:
application/json
- `null`
# 获取当前作用域路径
- **URL**:
/api/domain/user/path
- **Method**:
GET
- **query parameter**
- 无 - 只包含前置说明中提到的token, 之后不再重复说明。
- **Content-Type**:
application/json
- `null`
# 新增两清
新增两清数据,支持批量。批量能有效提高性能,所以能批量就批量。
## 接口名称
addFamily
>## 用法
- **URL:**/api/interface/user/addFamily/invoke
- **Method:** POST
> **Note:** <font color="#dd0000">Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分.</font>
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH002/XH002",/XH 代表徐汇区,/XH/XH002代表某个项目,其中/XH002 是项目编号
- **params:**
- **data** 新增的数据,必须是数组,即使只有一条数据。
```json
[{
beiChaiQianRen: string,----------------------被拆迁人
buWeiPingGuJieHeChongXinDanJia: string,------多房子,分开评估 11700元/㎡,房21200元/㎡
chongZhiJieHeChengXin: double,---------------多房子,评估成一个单价。
devices: [{----------------------------------设备
danJia:double,-----------------------------单价
jiLiang:double,----------------------------数量
name:string,-------------------------------名称
unit:string,-------------------------------单位,如个,平方米等
zongJia:double-----------------------------总价
}],
erTai: int,----------------------------------二胎
fangWuXingZhi: string,-----------------------房屋性质
fangWuZuoLuo: string,------------------------房屋坐落
fuShuWuJinE: double,-----------------------附属物金额
fuShuWus: [{---------------------------------附属物
danJia:double,-----------------------------单价
jieGou:string,-----------------------------结构
jiLiang:double,----------------------------数量
name:string,-------------------------------名称
type:string,-------------------------------类型
unit:string,-------------------------------单位
usage:string,------------------------------附属物用途
zongJia:double-----------------------------附属物总价
}],
heDingMianJi: double,------------------------核定面积
identityCard: string,------------------------被补偿人身份证号
juMinFenZu: string,--------------------------居民分组
leiXing: string,-----------------------------如居民户,农民户
number: string,------------------------------征收编号,合同编号
pingGuMianJi: double,------------------------评估面积
shiCeMianJi: double,-------------------------实测面积
tuDiZhengHao: string,------------------------土地证号
weiRenDingMianJi: double,--------------------未认定面积
wuZhengMJ: double,---------------------------无证面积
yingJianWeiJianMianJi: string,---------------应建未建面积
youZhengDuoYuMianJi: string,-----------------有证多余面积
youZhengMianJi: string,----------------------有证面积
zhaiJiDiShiYongZheng: string-----------------宅基地使用证
}]
```
## 返回值
`[int]` - 新增的两清的id
## 例子1
>**Note:** 测试时,例子中的参数token中的值不可以直接使用。
```json
{
"token": "r0GRdB_xSjijV8Um5mrAPIjOKzQM_V_MsBYAP26yH5utZnQkBQrki539pqV4eKcHj9q14mdFW4uZwlTabdYuRXjOQmOtc9DJV23KiWOrg1aNxTd_0UH6B9HieYIVG0lPSLUABQN6-B_ewlTdi7KxwCchO8iNAs7zCaz39UNc0Jg",
"dmPath": "/XH/XH002/XH002",
"params": {
"data": [
{
"erTai": 0,
"fuShuWus": [
{
"danJia": 9200,
"jiLiang": 1,
"name": "棚舍及附属物",
"zongJia": 9200
}
],
"number": "14HP0112",
"isLocked": false,
"beiChaiQianRen": "吴龙华(户)",
"identityCard": "310221195909183215",
"fangWuZuoLuo": "宅河头76号",
"leiXing": "农业户",
"fangWuXingZhi": "私房",
"youZhengMianJi": "178.0000",
"heDingMianJi": "301.0000",
"shiCeMianJi": "405.5000",
"chongZhiJieHeChengXin": "830.00",
"yingJianWeiJian": "0.00",
"youZhengDuoYuMianJi": "0.00",
"tuDiZhengHao": "",
"zhaiJiDiShiYongZheng": "沪集宅(上龙)字第华浦-330号",
"erCiDongQianMianJi": "0.00",
"heDingRenKou": "7.0000",
"zaiCeHuShu": "2.00"
}
]
}
}
```
## 例子2
```json
{
"token": "ZrFT8bbnQy-CkmL0_sGNModAE142dFiV5HmUcFA3Jm1yt3XZqjZa5Q8ifNvxi7EgInc46K4JnRwNodLbkSlSqO2v2UU7wdY24QwpDAjcBhzFQCXhwOYUVU8JKVCD85ps3PxnioIFuYkP6YZH_FdluTsl7siRin5yAxIMxCNWqI8",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [{
"erTai": 0,
"fuShuWus": [{
"danJia": 29051.0,
"jiLiang": 1.0,
"name": "",
"zongJia": 29051.0
}],
"number": "QPXQCJT-18",
"beiChaiQianRen": "杜进兴(户)",
"identityCard": "",
"fangWuZuoLuo": "新桥村51号",
"youZhengMianJi": "263.0600",
"heDingMianJi": "180.0000",
"juMinFenZu": "十一队",
"weiRenDingMianJi": "0.0000",
"pingGuMianJi": "263.06",
"buWeiPingGuJieHeChongXinDanJia": "主屋:758.00、附屋:673.00",
"fuShuWuJinE": "29051.00"
}]
}
}
```
\ No newline at end of file
# 新增房屋
新增房屋数据,支持批量。批量能有效提高性能,所以能批量就批量。
## 接口名称
addHouse
>## 用法
- **URL:**/api/interface/user/addHouse/invoke
- **Method:** POST
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 新增的数据,必须是数组,即使只有一条数据。
```json
[{
fid:int-----------------------------调用addFamily接口返回的ID,即房子归属于哪个合同
name:string,------------------------名称(房屋的名称)
zhengZaiMianJi:double,--------------证载面积
pingGuDanJia:double,----------------评估单价
pingGuJiaGe:double,-----------------评估价格
fangWuYongTu:string,----------------房屋用途
shiFouRenDing:string,---------------是否认定
yangTaiMianJi:double,---------------阳台面积
pengSheMianJi:double,---------------棚舍面积
chaoLing:string,--------------------是否超龄老房
fangWuLeiXing:string,---------------房屋类型
renDingMianJi:double,---------------认定面积
ceHuiMianJi:double,-----------------测绘面积
tengKongShiJian:timestamp-----------腾空时间
}]
```
## 返回值
`[int]` - 新增的房屋的id
## 例子
```json
{
"token": "Y61Cp_zXDYTPYiGYUEmhD0a4uYu7-9NsetKtQaR7Gs0XEOJScQ9Lk1-kFIH_MgK0zZgu_6BMLqJRtUhnS4HDuB_YvNsMcIX5cALlfWHpW_Vetjn2VCL4_k-7hbMVXsEbvscZUfMO-CkA1wh0MpH40oFweZao6YWSZG3Qg4AuWg4",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [{
"fid": "18155",
"ceHuiMianJi": 0.0,
"fangWuLeiXing": "",
"fangWuYongTu": "居住",
"name": "主屋",
"pengSheMianJi": 0.0,
"pingGuDanJia": 758.0,
"pingGuJiaGe": 141124.44,
"renDingMianJi": 0.0,
"shiFouRenDing": "是",
"tengKongShiJian": "0001-01-01T00:00:00",
"yangTaiMianJi": 0.0,
"zhengZaiMianJi": 186.18
},
{
"fid": "18155",
"ceHuiMianJi": 0.0,
"fangWuLeiXing": "",
"fangWuYongTu": "居住",
"name": "附屋",
"pengSheMianJi": 0.0,
"pingGuDanJia": 673.0,
"pingGuJiaGe": 51740.24,
"renDingMianJi": 0.0,
"shiFouRenDing": "是",
"tengKongShiJian": "0001-01-01T00:00:00",
"yangTaiMianJi": 0.0,
"zhengZaiMianJi": 76.88
}]
}
}
```
\ No newline at end of file
# 新增人口
新增人口数据,支持批量。批量能有效提高性能,所以能批量就批量。
## 接口名称
addPeople
>## 用法
- **URL:**/api/interface/user/addPeople/invoke
- **Method:** POST
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 新增的数据,必须是数组,即使只有一条数据。
```json
[{
fid: int ----------------------------------------Family表的ID,调用addFamily返回的
name: string,------------------------------------姓名
idCard: string,----------------------------------身份证号
sex: string,-------------------------------------性别
age: int,----------------------------------------年龄
chanQuanRen: boolean,----------------------------是否产权人
relationWithChanQuanRen: string,-----------------与产权人关系
huZhu: boolean,----------------------------------是否户主
relationWithHuZhu: string,-----------------------与户主关系
teKun: boolean,----------------------------------是否特困
daBing: boolean,---------------------------------是否大病
gaoLing: boolean,--------------------------------是否高龄
daLingWeiHun: boolean,---------------------------是否大龄未婚
canJi: boolean,----------------------------------是否残疾
zaiCe: boolean,----------------------------------是否在册
anZhi: boolean,----------------------------------是否安置
weiYun: boolean,---------------------------------是否未孕
duShengZiNv: boolean,----------------------------是否独生子女
birthDay: date,----------------------------------出生年月
cunZaiWeiHeDingPeiOu: boolean--------------------是否存在未核定配偶
}]
```
## 返回值
`[int]` - 新增的people的id
## 例子
```json
{
"token": "f_pIt_6oIF2qzNto6hw-hYqet8HdQKext5jvXnHa0omEgmNypxeYNbJRs3RJl7buITnUT3btpxgPiqajQe8Rrh01767hj-BFfo8ug-SDEUN9HU4JXI7x2B00S-4vE72w_k-8LAZ1wzBH3-a6ktXORSnq_kDAYIP7X3F3hvrxm48",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [
{
"fid": "18155",
"age": 0,
"birthDay": "1900/01/01",
"idCard": "410726199104048172",
"name": "吴某",
"relationWithChanQuanRen": "朋友",
"sex": "男",
"anZhi": "是",
"canJi": false,
"chanQuanRen": false,
"cunZaiWeiHeDingPeiOu": false,
"daBing": false,
"daLingWeiHun": false,
"duShengZiNv": false,
"huZhu": false,
"teKun": false,
"weiYun": false,
"zaiCe": "否",
"gaoLing": false
},
{
"fid": "18155",
"age": 0,
"birthDay": "1900/01/01",
"idCard": "37172419970404447X",
"name": "宇辰",
"relationWithChanQuanRen": "无",
"sex": "男",
"anZhi": "是",
"canJi": false,
"chanQuanRen": false,
"cunZaiWeiHeDingPeiOu": false,
"daBing": false,
"daLingWeiHun": false,
"duShengZiNv": false,
"huZhu": false,
"teKun": false,
"weiYun": false,
"zaiCe": "否",
"gaoLing": false
}
]
}
}
```
\ No newline at end of file
# 协议报审
报审协议支持批量报审
当数据全部进入系统,并锁定后,调用该接口,生成合同PDF 。
## 接口名称
approveXieYi
>## 用法
- **URL:**/api/interface/user/approveXieYi/invoke
- **Method:** POST
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 报审要传的参数名格式如下,其中1 2 3 为Family的id,即addFamily接口 返回的ID
```json
[{
1,2,3
}]
```
## 返回值
# 获取工作人员(上岗人员)
## 接口名称
getClerks
>## 用法
- **URL:**/api/interface/user/getClerks/invoke
- **Method:** Get
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **{}** 无参数,但是{}一定要有。
例如,用下面参数调用接口
```json
[{}]
```
## 返回值
接口的返回基本都是json格式,并且有如下形式:
```json
{
errorCode: long,---------------------为0表示成功
data: any,---------------------------返回的结果
message: string----------------------errorCode 为错误代码时候,显示错误信息
}
```
## 例子
>**note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
**Result**
```json
{
"errorCode": 0,
"data": [
{
"number": "222",
"name": "zhangsahou",
"id": 1095
},
{
"number": "333",
"name": "zhangsan",
"id": 1096
}
],
"message": ""
}
```
# 获取协议PDF
## 接口名称
getXieYiPDF
>## 用法
- **URL:**/api/interface/user/getXieYiPDF/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **id** 是主数据Family的ID
## 返回值
如果该id 对应的Family 已经报审,那么就会返回,合同的PDF文件下载路径。
如果参数id在系统中不存在或者数据未报审,才会返回错误。具体参看例子。
## 例子
### id存在且报审
>参数为`{"id":1348}`(省略了token,dmpath等参数)
- result
```json
{
"data": "http://222.73.234.155/bm/resource/vK6Yk1k94K7ZDrZmEFmyYwuWj8Nbg-_Hj79xzq-GQruyzZVoH6tVfKHBFGdtOHuH-95ZMZmON0OXyMpqUBrLi07Z09U5PYU9B2CvxW8QsAjgPXPIWT8kTh3rGd9PsvnbWO0SvnZ8TSJo3uLN9BfqApgtcUvIJ4YGC4oUajPFC30/emb%3A%2F%2F1508",
"errorCode": 0
}
```
- 特别注意:返回的链接是有时效性的,目前是半小时后失效。如时间失效,需要重新调用接口获取。
### id不存在
>参数为`{"id":1349}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 65536,
"message": "无效的id或协议未报审"
}
```
- 特别注意:返回的链接是有时效性的,目前是半小时后失效。如时间失效,需要重新调用接口获取。
### id存在,协议未报审
>参数为`{"id":1348}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 65536,
"message": "无效的id或协议未报审"
}
```
- 特别注意:返回的链接是有时效性的,目前是半小时后失效。如时间失效,需要重新调用接口获取。
\ No newline at end of file
# 录入房源
录入、更新房源数据。在选房之前,房子数据要进入系统。系统中房源以房源编号(number)为唯一标识。
## 接口名称
importFangYuan
>## 用法
- **URL:**/api/interface/user/importFangYuan/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 是FangYuan的数据
- **bindPath** 把房子绑定到某个项目中.
```json
[{
number:string,-------------------------------房源编号(主键,必须唯一)
type:string,---------------------------------房源类别
nature:string,-------------------------------性质
peiTao:string,-------------------------------是否配套
qiOrXian:string,-----------------------------期房现房
laiYuan:string,------------------------------来源
huXing:string,-------------------------------户型
taoXing:string,------------------------------套型
jieGou:string,-------------------------------房源结构
usage:string,--------------------------------用途
yuCeMianJi:double,---------------------------预测面积
yuCeYangTaiMianJi:double,--------------------预测阳台面积
shiCeMianJi:double,--------------------------实测面积
shiCeYangTaiMianJi:double,-------------------实测阳台面积
sheJiMianJi:double,--------------------------设计面积
sheJiYangTaiMianJi:double,-------------------设计阳台面积
usedAreaType:string,-------------------------使用面积类型
usedArea:double,-----------------------------使用面积
danJia:double,-------------------------------单价
youHuiDanJia:double,-------------------------优惠单价
zongJia:double,------------------------------房屋总价
youHuiZongJia:double,------------------------优惠总价
shiCeZongJia:double,-------------------------实测总价
district:string,-----------------------------所属区域
subDistrict:string,--------------------------街道名
community:string,----------------------------小区名称
lane:string,---------------------------------弄
building:string,-----------------------------幢、栋
unit:string,---------------------------------单元
no:string,-----------------------------------号
floor:string,--------------------------------楼层
room:string,---------------------------------室号
contractNumber:,-----------------------------联系电话
diKuai:string,-------------------------------地块名称
address:string,------------------------------地址
jinHuRiQi:date,------------------------------进户日期
yuJiJiaoFangRiQi:date,-----------------------预计交房日期
shiJiJiaoFangRiQi:date,----------------------实际交房日期
}]
```
## 返回值
## 例子
```json
{
"token": "RNZWJeF2n5pOlLPuHr8xFZNT5sH3HMEmD75axJ_n17swh6yv-oRV1Jr3GRhn1gLWpGGtQYvC9r9-yBteXmg-qDxp2Ln70R9gBEMo95vxcHShLPNWTp3USqUglRs2XqHKQrToyw5pwBjJsv_UxZzrT30DQPsr07cemMi99QBXVYY",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [
{
"anZhiMianJi": 0,
"building": "",
"community": "",
"danJia": 3400,
"district": "浦东新区",
"floor": "",
"huXing": "",
"jieGou": "",
"laiYuan": "",
"lane": "",
"nature": "",
"no": "",
"number": "cs-005",
"peiTao": "",
"qiOrXian": "期房",
"room": "",
"sheJiMianJi": 0,
"sheJiYangTaiMianJi": 0,
"shiCeMianJi": 0,
"shiCeYangTaiMianJi": 0,
"shiCeZongJia": 0,
"shiJiJiaoFangRiQi": "0001/01/01",
"subDistrict": "",
"taoXing": "中套",
"unit": "",
"usedArea": 85.12,
"usedAreaType": "预测面积",
"youHuiDanJia": 0,
"youHuiZongJia": 0,
"yuCeMianJi": 0,
"yuCeYangTaiMianJi": 0,
"yuJiJiaoFangRiQi": "1900/01/01",
"zongJia": 0
},
{
"anZhiMianJi": 0,
"building": "",
"community": "",
"danJia": 3400,
"district": "浦东新区",
"floor": "",
"huXing": "",
"jieGou": "",
"laiYuan": "",
"lane": "",
"nature": "",
"no": "",
"number": "cs-012",
"peiTao": "",
"qiOrXian": "期房",
"room": "",
"sheJiMianJi": 0,
"sheJiYangTaiMianJi": 0,
"shiCeMianJi": 0,
"shiCeYangTaiMianJi": 0,
"shiCeZongJia": 0,
"shiJiJiaoFangRiQi": "0001/01/01",
"subDistrict": "",
"taoXing": "小大套",
"unit": "",
"usedArea": 96.51,
"usedAreaType": "预测面积",
"youHuiDanJia": 0,
"youHuiZongJia": 0,
"yuCeMianJi": 0,
"yuCeYangTaiMianJi": 0,
"yuJiJiaoFangRiQi": "1900/01/01",
"zongJia": 0
}
],
"bindPath": "/XH/XH2018003/XH2018003-01"
}
}
```
\ No newline at end of file
import React from 'react';
import cs from 'classnames';
import ReactMarkdown from 'react-markdown';
import { Spin } from 'antd';
import { AsyncComponent } from 'react-async-wrapper';
import { userApi } from '../../../../../services/interfaces';
import Code from '../code';
import styles from '../markdown.less';
import { processError } from '../../../../../utils/error';
const MarkDownWithLoading = ({ loading, source, height }) => (
<Spin spinning={loading} style={{ height, maxHeight: height }}>
<ReactMarkdown
source={source}
className={cs({
'markdown-body': true,
[styles.markdown]: true,
})}
renderers={{
code: Code,
}}
/>
</Spin>
);
export default (match, infoes, size) => {
const pathBase = `${match.path}/interface`;
// return [
// ['addFamily', '新增两清'],
// ['addHouse', '新增房屋'],
// ['addPeople', '新增人口'],
// ['approveXieYi', '协议报审'],
// ['getClerks', '获取经办人'],
// ['getXieYiPdf', '获取协议pdf'],
// ['importFangYuan', '导入房源'],
// ['isLiangQingLocked', '查询两清是否锁定'],
// ['isXieYiApproved', '查询协议是否审核'],
// ['isXieYiLocked', '查询协议是否锁定'],
// ['lockLiangQing', '锁定两清'],
// ['lockXieYi', '锁定协议'],
// ['removeFamily', '删除两清'],
// ['removeFangYuan', '删除房源'],
// ['removeHouse', '删除房屋'],
// ['removePeople', '删除人口'],
// ['requestCancelXieYi', '申请撤销协议'],
// ['requestUnLockLiangQing', '申请解锁两清'],
// ['updateFamily', '更新两清'],
// ['updateHouse', '更新房屋'],
// ['updatePeople', '更新人口'],
// ['updateXieYi', '更新协议'],
// ['whetherCancelXieYi', '是否协议已撤销'],
// ['whetherUnLockLiangQing', '是否两清已解锁'],
// ]
return infoes.map(({ name, showName }) => ({
name,
showName: showName || name,
key: `interface/${name}`,
path: `${pathBase}/${name}`,
// component: makeAsync({})(import(`./${name}.md`).then(content => content.default || content).then(md)),
render: () => (
<div style={{
padding: 12,
background: '#fafafa',
width: '100%',
height: size.height,
overflow: 'scroll',
}}
>
<AsyncComponent
batch
onError={processError}
asyncProps={{
source: async () => userApi.getInterfaceDocument(name),
}}
>
<MarkDownWithLoading height={size.height - 24} />
</AsyncComponent>
</div>
),
}));
};
# 动态接口
动态接口可以可以看作函数,有参数,有返回值。
具体执行的内容在后台由技术人员进行配置,
理论上任何任务都可以通过这个api完成。
因为是可配置的,所以称之为**动态**的,
不过一般情况下,进入生产环境配置好的接口不会轻易改动,
除非有bug。
- **URL:** /api/interface/user/{name}/invoke
- *name* - 动态接口的名称,可以认为是动态接口的唯一标识符。
- **Method:** POST
- **Accept:** application/json
- *params* - **object** 动态接口的参数,类似函数的参数。
任意键值对,键为字符串,值为任意合法的**json**值类型。
具体的形式依赖于当前动态接口的配置。
- **Content-Type:** application/json
- \- **any** 返回值没有固定类型,完全依赖于配置。
# 查询两清是否锁定
## 接口名称
isLiangQingLocked
>## 用法
- **URL:**/api/interface/user/isLiangQingLocked/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 查询两清是否锁定要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
返回data 中的值
- 0 代表未锁定
- -1 代表数据不存在
- 1 代表已锁定
## 例子
### id存在
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0
],
"errorCode": 0
}
```
### id不存在
>参数为`{"data":[1815]}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
-1
],
"errorCode": 0
}
```
### 多个id
>参数为`{"data":[18155,1]}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0,
-1
],
"errorCode": 0
}
```
\ No newline at end of file
# 查询协议是否正式协议
## 接口名称
isXieYiApproved
>## 用法
- **URL:**/api/interface/user/isXieYiApproved/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 查询当前协议是不是报审状态要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
返回data 中的值
- 0 代表未报审(非正式协议,无法获取合同pdf 文件)
- -1 代表数据不存在
- 1 代表已报审(正式协议)
## 例子
### id存在
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0
],
"errorCode": 0
}
```
### id不存在
>参数为`{"data":0}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
-1
],
"errorCode": 0
}
```
### 多个id
>参数为`{"data":[18155,1]}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0,
-1
],
"errorCode": 0
}
```
\ No newline at end of file
# 查询协议是否锁定
## 接口名称
isXieYiLocked
>## 用法
- **URL:**/api/interface/user/isXieYiLocked/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 查询当前协议是不是锁定状态要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
返回data 中的值
- 0 代表未锁定
- -1 代表数据不存在
- 1 代表已锁定
## 例子
### id存在
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0
],
"errorCode": 0
}
```
### id不存在
>参数为`{"data":0}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
-1
],
"errorCode": 0
}
```
### 多个id
>参数为`{"data":[18155,1]}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0,
-1
],
"errorCode": 0
}
```
\ No newline at end of file
# 锁定两清
## 接口名称
lockLiangQing
>## 用法
- **URL:**/api/interface/user/lockLiangQing/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 锁定两清要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
## 例子
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 0
}
说明 锁定两清成功
```
>参数为`{"data":0}`(省略了token,dmpath等参数)
- result
```json
{
"data": 0,
"errorCode": 268435457,
"message": "无效的id:0"
}
```
\ No newline at end of file
# 锁定协议
## 接口名称
lockXieYi
>## 用法
- **URL:**/api/interface/user/lockXieYi/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 锁定协议要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
## 例子
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 0
}
说明 锁定两清成功
```
>参数为`{"data":0}`(省略了token,dmpath等参数)
- result
```json
{
"data": 0,
"errorCode": 268435457,
"message": "无效的id:0"
}
```
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": 18155,
"errorCode": 268435458,
"message": "请先锁定两清,再锁定协议"
}
```
# 删除两清
删除两清是将系统中对应的两清数据以及关联数据,全部删除。
## 接口名称
removeFamily
>## 用法
- **URL:**/api/interface/user/removeFamily/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 删除两清要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
## 例子
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 65555,
"message": "两清信息已锁定,请申请解锁后再更改"
}
```
>参数为`{"data":222}`(省略了token,dmpath等参数)
- result
```json
{
"data": 222,
"errorCode": 268435457,
"message": "无效的id:222"
}
```
\ No newline at end of file
# 删除房源
## 接口名称
removeFangYuan
>## 用法
- **URL:**/api/interface/user/removeFangYuan/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 删除房源要传的参数名格式如下,其中1 2 3 是 房源 id
```json
[{
1,2,3
}]
```
## 返回值
## 例子
>参数为`{"data":"cs-005"}`(省略了token,dmpath等参数)
- result
```json
{
"data": 0,
"errorCode": 0
}
data 为0表示没有删除成功
```
# 删除被补偿房屋
删除被补偿房屋数据,这里的被补偿房屋,就是要征收或拆迁的房子,不是安置房屋。
## 接口名称
removeHouse
>## 用法
- **URL:**/api/interface/user/removeHouse/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 删除被补偿房屋要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
## 例子
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": 18155,
"errorCode": 268435457,
"message": "无效的id: 18155"
}
```
>参数为`{"data":18251}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 65555,
"message": "两清信息已锁定,请申请解锁后再更改"
}
```
>参数为`{"data":18251}`(省略了token,dmpath等参数)
- result
```json
{
"data":0,
"errorCode": 0
}
删除成功
```
# 删除人口
## 接口名称
removePeople
>## 用法
- **URL:**/api/interface/user/removePeople/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 删除人口要传的参数名格式如下,其中1 2 3 是 两清 id
```json
[{
1,2,3
}]
```
## 返回值
## 例子
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"errorCode": 65555,
"message": "两清信息已锁定,请申请解锁后再更改"
}
```
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": 222,
"errorCode": 268435457,
"message": "无效的id:18155"
}
```
# 申请撤销协议
## 接口名称
requestCancleXieYi
>## 用法
- **URL:**/api/interface/user/requestCancleXieYi/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data**
- **what** 申请的两清的id
- **why** 申请的理由
- **applier** 申请人
- **extra** 备注
## 返回值
返回生成的申请记录的ID
## 例子
### waht存在
>参数为
```json
{
"data": [{
"what": 18155,
"why": "接口测试",
"applier": "操作员",
"extra": "测试"
}]
}
(省略了token,dmpath等参数)
```
- result
```json
{
"data": [
18351
],
"errorCode": 0
}
```
- 特别注意:data里面的值是申请记录的id
### what 中的值不存在
>参数为
```json
{
"data": [{
"what": 1814,
"why": "接口测试",
"applier": "操作员",
"extra": "测试"
}]
}
(省略了token,dmpath等参数)
```
- result
```json
{
"data": 1814,
"errorCode": 268435457,
"message": "无效的id:1814"
}
```
\ No newline at end of file
# 申请解锁两清
## 接口名称
requestUnLockLiangQing
>## 用法
- **URL:**/api/interface/user/requestUnLockLiangQing/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data**
- **what** 申请的两清的id
- **why** 申请的理由
- **applier** 申请人
- **extra** 备注
## 返回值
返回生成的申请记录的ID
## 例子
### waht存在
>参数为
```json
{
"data": [{
"what": 18155,
"why": "接口测试",
"applier": "操作员",
"extra": "测试"
}]
}
(省略了token,dmpath等参数)
```
- result
```json
{
"data": [
18351
],
"errorCode": 0
}
```
- 特别注意:data里面的值是申请记录的id
### what 中的值不存在
>参数为
```json
{
"data": [{
"what": 1814,
"why": "接口测试",
"applier": "操作员",
"extra": "测试"
}]
}
(省略了token,dmpath等参数)
```
- result
```json
{
"data": 1814,
"errorCode": 268435457,
"message": "无效的id:1814"
}
```
# 更新两清
更新两清数据,支持批量。批量能有效提高性能,所以能批量就批量。
## 接口名称
updateFamily
>## 用法
- **URL:**/api/interface/user/updateFamily/invoke
- **Method:** POST
> **Note:** <font color="#dd0000">Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分.</font>
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH002/XH002",/XH 代表徐汇区,/XH/XH002代表某个项目,其中/XH002 是项目编号
- **params:**
- **data** 新增的数据,必须是数组,即使只有一条数据。
```json
[{
beiChaiQianRen: string,----------------------被拆迁人
buWeiPingGuJieHeChongXinDanJia: string,------多房子,分开评估 11700元/㎡,房21200元/㎡
chongZhiJieHeChengXin: double,---------------多房子,评估成一个单价。
devices: [{----------------------------------设备
danJia:double,-----------------------------单价
jiLiang:double,----------------------------数量
name:string,-------------------------------名称
unit:string,-------------------------------单位,如个,平方米等
zongJia:double-----------------------------总价
}],
erTai: int,----------------------------------二胎
fangWuXingZhi: string,-----------------------房屋性质
fangWuZuoLuo: string,------------------------房屋坐落
fuShuWuJinE: double,-----------------------附属物金额
fuShuWus: [{---------------------------------附属物
danJia:double,-----------------------------单价
jieGou:string,-----------------------------结构
jiLiang:double,----------------------------数量
name:string,-------------------------------名称
type:string,-------------------------------类型
unit:string,-------------------------------单位
usage:string,------------------------------附属物用途
zongJia:double-----------------------------附属物总价
}],
heDingMianJi: double,------------------------核定面积
identityCard: string,------------------------被补偿人身份证号
juMinFenZu: string,--------------------------居民分组
leiXing: string,-----------------------------如居民户,农民户
number: string,------------------------------征收编号,合同编号
pingGuMianJi: double,------------------------评估面积
shiCeMianJi: double,-------------------------实测面积
tuDiZhengHao: string,------------------------土地证号
weiRenDingMianJi: double,--------------------未认定面积
wuZhengMJ: double,---------------------------无证面积
yingJianWeiJianMianJi: string,---------------应建未建面积
youZhengDuoYuMianJi: string,-----------------有证多余面积
youZhengMianJi: string,----------------------有证面积
zhaiJiDiShiYongZheng: string-----------------宅基地使用证
}]
```
## 返回值
## 例子1
```json
{
"token": "r0GRdB_xSjijV8Um5mrAPIjOKzQM_V_MsBYAP26yH5utZnQkBQrki539pqV4eKcHj9q14mdFW4uZwlTabdYuRXjOQmOtc9DJV23KiWOrg1aNxTd_0UH6B9HieYIVG0lPSLUABQN6-B_ewlTdi7KxwCchO8iNAs7zCaz39UNc0Jg",
"dmPath": "/XH/XH002/XH002",
"params": {
"data": [
{
"id":18155,
"erTai": 0,
"fuShuWus": [
{
"danJia": 9200,
"jiLiang": 1,
"name": "棚舍及附属物",
"zongJia": 9200
}
],
"number": "14HP0112",
"isLocked": false,
"beiChaiQianRen": "吴龙华(户)",
"identityCard": "310221195909183215",
"fangWuZuoLuo": "宅河头76号",
"leiXing": "农业户",
"fangWuXingZhi": "私房",
"youZhengMianJi": "178.0000",
"heDingMianJi": "301.0000",
"shiCeMianJi": "405.5000",
"chongZhiJieHeChengXin": "830.00",
"yingJianWeiJian": "0.00",
"youZhengDuoYuMianJi": "0.00",
"tuDiZhengHao": "",
"zhaiJiDiShiYongZheng": "沪集宅(上龙)字第华浦-330号",
"erCiDongQianMianJi": "0.00",
"heDingRenKou": "7.0000",
"zaiCeHuShu": "2.00"
}
]
}
}
```
>**Note:** 测试时,例子中的参数token中的值不可以直接使用。本接口在使用时,最好调用一下isLiangQingLocked 接口,判断两情数据是否已锁定,如果数据状态是已锁定状态,那么数据更新会失败,
result
```json
{
"errorCode": 65555,
"message": "两清信息已锁定,请申请解锁后再更改"
}
```
## 例子2
```json
{
"token": "ZrFT8bbnQy-CkmL0_sGNModAE142dFiV5HmUcFA3Jm1yt3XZqjZa5Q8ifNvxi7EgInc46K4JnRwNodLbkSlSqO2v2UU7wdY24QwpDAjcBhzFQCXhwOYUVU8JKVCD85ps3PxnioIFuYkP6YZH_FdluTsl7siRin5yAxIMxCNWqI8",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [
{
"id": 18155,
"erTai": 0,
"fuShuWus": [
{
"danJia": 29051,
"jiLiang": 1,
"name": "",
"zongJia": 29051
}
],
"number": "QPXQCJT-18",
"beiChaiQianRen": "杜进兴(户)",
"identityCard": "",
"fangWuZuoLuo": "新桥村51号",
"youZhengMianJi": "263.0600",
"heDingMianJi": "180.0000",
"juMinFenZu": "十一队",
"weiRenDingMianJi": "0.0000",
"pingGuMianJi": "263.06",
"buWeiPingGuJieHeChongXinDanJia": "主屋:758.00、附屋:673.00",
"fuShuWuJinE": "29051.00"
}
]
}
}
```
\ No newline at end of file
# 更新被补偿房屋
更新被补偿房屋数据,支持批量。被补偿房屋指的是将要征收或拆迁的房子,不是安置房源
## 接口名称
updateHouse
>## 用法
- **URL:**/api/interface/user/updateHouse/invoke
- **Method:** POST
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 新增的数据,必须是数组,即使只有一条数据。
```json
[{
id:int-----------------------------House(被补偿房屋)的ID
name:string,------------------------名称(房屋的名称)
zhengZaiMianJi:double,--------------证载面积
pingGuDanJia:double,----------------评估单价
pingGuJiaGe:double,-----------------评估价格
fangWuYongTu:string,----------------房屋用途
shiFouRenDing:string,---------------是否认定
yangTaiMianJi:double,---------------阳台面积
pengSheMianJi:double,---------------棚舍面积
chaoLing:string,--------------------是否超龄老房
fangWuLeiXing:string,---------------房屋类型
renDingMianJi:double,---------------认定面积
ceHuiMianJi:double,-----------------测绘面积
tengKongShiJian:timestamp-----------腾空时间
}]
```
## 返回值
## 例子
```json
{
"token": "Y61Cp_zXDYTPYiGYUEmhD0a4uYu7-9NsetKtQaR7Gs0XEOJScQ9Lk1-kFIH_MgK0zZgu_6BMLqJRtUhnS4HDuB_YvNsMcIX5cALlfWHpW_Vetjn2VCL4_k-7hbMVXsEbvscZUfMO-CkA1wh0MpH40oFweZao6YWSZG3Qg4AuWg4",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [{
"id": "18251",
"ceHuiMianJi": 0.0,
"fangWuLeiXing": "",
"fangWuYongTu": "居住",
"name": "主屋",
"pengSheMianJi": 0.0,
"pingGuDanJia": 758.0,
"pingGuJiaGe": 141124.44,
"renDingMianJi": 0.0,
"shiFouRenDing": "是",
"tengKongShiJian": "1900/01/01",
"yangTaiMianJi": 0.0,
"zhengZaiMianJi": 186.18
},
{
"id": "18155",
"ceHuiMianJi": 0.0,
"fangWuLeiXing": "",
"fangWuYongTu": "居住",
"name": "附屋",
"pengSheMianJi": 0.0,
"pingGuDanJia": 673.0,
"pingGuJiaGe": 51740.24,
"renDingMianJi": 0.0,
"shiFouRenDing": "是",
"tengKongShiJian": "2017/01/08",
"yangTaiMianJi": 0.0,
"zhengZaiMianJi": 76.88
}]
}
}
```
\ No newline at end of file
# 更新人口
更新人口数据,支持批量。批量能有效提高性能,所以能批量就批量。
## 接口名称
updatePeople
>## 用法
- **URL:**/api/interface/user/updatePeople/invoke
- **Method:** POST
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 新增的数据,必须是数组,即使只有一条数据。
```json
[{
fid: string,-------------------------------------Family(两清)的ID
name: string,------------------------------------姓名
idCard: string,----------------------------------身份证号
sex: string,-------------------------------------性别
age: int,----------------------------------------年龄
chanQuanRen: boolean,----------------------------是否产权人
relationWithChanQuanRen: string,-----------------与产权人关系
huZhu: boolean,----------------------------------是否户主
relationWithHuZhu: string,-----------------------与户主关系
teKun: boolean,----------------------------------是否特困
daBing: boolean,---------------------------------是否大病
gaoLing: boolean,--------------------------------是否高龄
daLingWeiHun: boolean,---------------------------是否大龄未婚
canJi: boolean,----------------------------------是否残疾
zaiCe: boolean,----------------------------------是否在册
anZhi: boolean,----------------------------------是否安置
weiYun: boolean,---------------------------------是否未孕
duShengZiNv: boolean,----------------------------是否独生子女
birthDay: date,----------------------------------出生年月
cunZaiWeiHeDingPeiOu: boolean--------------------是否存在未核定配偶
}]
```
## 返回值
## 例子
```json
{
"token": "f_pIt_6oIF2qzNto6hw-hYqet8HdQKext5jvXnHa0omEgmNypxeYNbJRs3RJl7buITnUT3btpxgPiqajQe8Rrh01767hj-BFfo8ug-SDEUN9HU4JXI7x2B00S-4vE72w_k-8LAZ1wzBH3-a6ktXORSnq_kDAYIP7X3F3hvrxm48",
"dmPath": "/XH/XH2018003/XH2018003-01",
"params": {
"data": [
{
"id": "18301",
"age": 0,
"birthDay": "1900/01/01",
"idCard": "410726199104048172",
"name": "吴某",
"relationWithChanQuanRen": "朋友",
"sex": "男",
"anZhi": "是",
"canJi": false,
"chanQuanRen": false,
"cunZaiWeiHeDingPeiOu": false,
"daBing": false,
"daLingWeiHun": false,
"duShengZiNv": false,
"huZhu": false,
"teKun": false,
"weiYun": false,
"zaiCe": "否",
"gaoLing": false
},
{
"id": "18302",
"age": 0,
"birthDay": "1900/01/01",
"idCard": "37172419970404447X",
"name": "宇辰",
"relationWithChanQuanRen": "无",
"sex": "男",
"anZhi": "是",
"canJi": false,
"chanQuanRen": false,
"cunZaiWeiHeDingPeiOu": false,
"daBing": false,
"daLingWeiHun": false,
"duShengZiNv": false,
"huZhu": false,
"teKun": false,
"weiYun": false,
"zaiCe": "否",
"gaoLing": false
}
]
}
}
```
>**Note:** 测试时,例子中的参数token中的值不可以直接使用。本接口在使用时,最好调用一下isLiangQingLocked 接口,判断两情数据是否已锁定,如果数据状态是已锁定状态,那么数据更新会失败,
result
```json
{
"errorCode": 65555,
"message": "两清信息已锁定,请申请解锁后再更改"
}
```
\ No newline at end of file
# 更新协议
更新协议数据,支持批量。
用来更新补偿数据,包括房屋价值补偿款,奖励补贴,安置房源等数据。如果补偿数据锁定,调用接口会更新不成功。
调用该接口,数据需要是未锁定的
## 接口名称
updateXieYi
## 前置条件
- 必须指定fid
- 只有未锁定的补偿数据才能进行修改
## 参数
- data - 更新的数据,必须是数组,即使只有一条数据。
```json
[{
fid:int--------------------------------------------FamilyID ,就是调用addFamily 返回的那个ID
anZhiFangShi:string,-------------------------------安置方式
zhuangHuangBuTie:double, --------------------------装潢补贴
zhuangHuangBuTieGongShi:string, -------------------装潢补贴公式
fangWuJiaZhiBuChangKuan:double, -------------------房屋价值补偿款
fangWuJiaZhiBuChangKuanGongShi:string, ------------房屋价值补偿款公式
weiRenDingMianJiZongJia:double, -------------------未认定面积总价
weiRenDingMianJiZongJiaGongShi:string, ------------未认定面积总价公式
fuShuWuJinE:double, -------------------------------附属物金额
fuShuWuGongShi:string,-----------------------------附属物
tingChanTingYeBuTie:double,------------------------停产停业补贴
tingChanTingYeBuTieGongShi:string,-----------------停产停业补贴公式
qianYueRiQi:date,----------------------------------签约日期
banQianRiQi:date,----------------------------------搬迁日期
zuiHouJinE:double,---------------------------------合同最后金额
daiLiRens:string,----------------------------------代理人
chaJiaJieSuan:double,------------------------------差价结算(房屋价值补偿款和安置房源的接口)
clauses:[{-----------------------------------------附加条款
content:string,--------------------------------附加条款内容
title:string-----------------------------------附加条款标题
}],
jiangLiBuTies:[{-----------------------------------奖励补贴
formula:string,--------------------------------奖励补贴公式
money:double,----------------------------------奖励补贴金额
name:string,-----------------------------------奖励补贴名称
order:int,-------------------------------------奖励补贴顺序
type:string------------------------------------奖励补贴类型(奖励or补助)
}],
anZhiFangWus:[{------------------------------------安置房屋
'order',int------------------------------------序号
'address',string-------------------------------地址
'area',double----------------------------------面积
'areaType',------------------------------------面积类型(建筑面积,预测面积,设计面积)
'fangWuDanJia',--------------------------------房屋单价
'fangWuZongJia',-------------------------------房屋总价
'youHuiDanJia',--------------------------------优惠单价
'youHuiZongJia',-------------------------------优惠总价
'shiChangDanJia',------------------------------市场单价
'shiChangZongJia',-----------------------------市场总价
'yuJiJiaoFangRiQi',----------------------------预计交房日期
'fangJiaGongShi',------------------------------房价公式(每套房子的钱可能是根据口径一套一套算出的金额)
'sheJiYangTaiMianJi',--------------------------设计阳台面积
'shiCeYangTaiMianJi',--------------------------实测阳台面积
'yuCeYangTaiMianJi'----------------------------预测阳台面积
}],
jingBanRens:[{
id:int------------------------------------------经办人的ID
}]
}]
```
## 返回值
# 申请撤销协议的处理状态
申请撤销协议发出后,由市局工作人员,进行处理,同意撤销或者不同意撤销。
## 接口名称
whetherCancelXieYi
>## 用法
- **URL:**/api/interface/user/whetherCancelXieYi/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 查询当前协议申请协议撤销市局人员是否已经处理,要传的参数名格式如下,其中1 2 3 是 申请记录的ID id
```json
[{
1,2,3
}]
```
## 返回值
返回data 中的值
- 0 代表协议撤销申请中
- -1 代表该条申请撤销不存在
- 1 协议撤销审核通过
- 2 协议撤销审核不通过
## 例子
### id存在
>参数为`{"data":18155}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0
],
"errorCode": 0
}
```
### id不存在
>参数为`{"data":0}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
-1
],
"errorCode": 0
}
```
### 多个id
>参数为`{"data":[18155,1]}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0,
-1
],
"errorCode": 0
}
```
\ No newline at end of file
# 申请解锁两清是否处理
申请解锁两清后,市局人员进行相关处理,同意解锁,或者不同意。返回处理的状态。
## 接口名称
whetherUnLockLiangQing
>## 用法
- **URL:**/api/interface/user/whetherUnLockLiangQing/invoke
- **Method:** Post
> **Note:** Domain 必须切换到基地这一作用域,切换作用域,参照“作用域”部分
## 参数
- **token:** token是在登陆后获取的tokenId,加密后的结果。
- **dmPath:** 作用域路径 例如 "/XH/XH2018003/XH2018003-01",/XH 代表徐汇区,/XH/XH2018003代表某个项目,其中XH2018003 是项目编号
- **params:**
- **data** 查询当前两清申请解锁市局人员是否已经处理,要传的参数名格式如下,其中1 2 3 是 申请记录的ID id
```json
[{
1,2,3
}]
```
## 返回值
返回data 中的值
- 0 代表两清解锁申请中
- -1 代表该条两情申请不存在
- 1 两情解锁审核通过
- 2 两清解锁审核不通过
## 例子
### id存在
>参数为`{"data":18352}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0
],
"errorCode": 0
}
```
### id不存在
>参数为`{"data":0}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
-1
],
"errorCode": 0
}
```
### 多个id
>参数为`{"data":[18352,1]}`(省略了token,dmpath等参数)
- result
```json
{
"data": [
0,
-1
],
"errorCode": 0
}
```
\ No newline at end of file
# 错误码
名称 | 值 | 说明
--------------------- | ----------- | ------
SUCCESS | 0x00000000L | 成功
EXCEPTION | 0x00010000L | 服务器内部异常,错误信息即异常的堆栈信息
NO_SUCH_USER | 0x00010001L | 用户不存在
WRONG_PASSWORD | 0x00010002L | 密码错误
INVALID_TOKEN | 0x00010003L | 无效的令牌,可能令牌已过期,或者令牌格式不对导致解析失败
NOT_AUTHED_YET | 0x00010012L | 令牌未经验证
STATE_VALIDATE_FAILED | 0x00010013L | 状态验证失败,调用动态接口时,若不满足必须的状态条件,则返回此错误码
PARAM_VALIDATE_FAILED | 0x00010014L | 参数验证失败,调用动态接口时,若不满足必须的参数条件,则返回此错误码
NO_OPERATION_RIGHT | 0x00010100L | 没有对应管理权限
NO_MODULE_RIGHT | 0x00010101L | 没有对应模块权限
NO_INTERFACE_RIGHT | 0x00010102L | 没有对应动态接口权限
NO_RESOURCE_RIGHT | 0x00010104L | 没有对应文件操作权限
NO_DOMAIN_RIGHT | 0x00010105L | 没有对应作用域权限
NO_DATASOURCE_RIGHT | 0x00010106L | 没有对应数据源权限
GENERAL_ERROR | 0x00011111L | 其他错误,没有特定意义。
CUSTOM_ERROR | 0x1XXXXXXXL | 自定义错误,X可以是任何值,动态接口中临时定义的错误,所以同样的错误码在不同动态接口中可能表示不同含义。
# 文件传输
本系统提供对小文件的存取功能。文件上传和文件的元信息的修改是分开进行的。
文件成功上传后会生成一个唯一的内部路径,用以唯一标识这个文件。
## 首次上传
### url
- **URL**:
/resource/{token}
- *token* 加密后的令牌
- **Method**:
POST
- **Content-Type**:
application/json
- *uriList* - **[ string ]** 当上传成功时,返回所上传文件的系统内部路径,
因为支持一次上传多个文件,所以返回的内部路径是个数组
- **or** *error object* - **object** 当上传失败时,则返回这个错误对象。
- *errorCode* - **long** 错误码
- *message* - **string** 错误信息
- *data* - **any** 错误相关数据
上传接口使用最普遍的multipart/form-data Post文件上传方式。
不同于其他接口,http包体的类型不再是application/json,而是multipart/form-data。
另一方面,加密的token也不再是放在包体中,而是直接拼接在url的路径中。
文件名若带后缀,系统支持自动根据后缀获取文件的媒体类型。
## 更新上传
### url
- **URL**:
/resource/{token}/{uri}
- *token* 加密后的令牌
- *uri* urlEncode过的系统内部路径
- **Method**:
POST
- **Content-Type**:
application/json
- *uriList* - **[ string ]** 当上传成功时,返回所上传文件的系统内部路径,
因为支持一次上传多个文件,所以返回的内部路径是个数组
- **or** *error object* - **object** 当上传失败时,则返回这个错误对象。
- *errorCode* - **long** 错误码
- *message* - **string** 错误信息
- *data* - **any** 错误相关数据
替换之前上传的文件。基本和首次上传相同,但因为需要替换的文件的内部uri是拼接在url上的。
所以一次只能替换一个文件,不再像首次上传那样,支持多个文件。只有管理员和文件的拥有者有权更新文件。
首次上传文件的用户默认为文件的拥有者。
import route from '../../../../components/hoc/routes';
import Main from './main';
export default () => route({
childRoutes: [{
path: 'main',
name: '文档',
component: Main,
}],
});
.main {
width:100%;
position: relative;
margin:0 auto;
background:rgb(250, 250, 250);
.catalogue {
font-size: 16px;
font-weight: bold;
margin-top: 1em;
list-style-type: cjk-ideographic;
li{
margin-bottom: 15px;
}
li ul {
margin-top: 10px;
}
}
.leftMenu {
width:300px;
height: 100%;
padding-top: 12px;
padding-bottom: 12px;
padding-left: 8px;
float: left;
overflow-y: scroll;
}
.contents {
margin-left: 250px;
height: 100%;
overflow-y: scroll;
}
}
# 前置说明
1. 接口的返回基本都是json格式,并且有如下形式:
```
{
errorCode: long,
data: any,
message: string
}
```
- *errorCode*: 错误码,0表示成功,无错误
- *data*: 真正的返回数据,后续文档如无特别说明,对于返回形式的描述都是指的data的格式。
另外,发生错误时,这个字段也可能保护错误相关信息。
- *message*: 错误消息,`errorCode`为0时,不使用这个字段。
2. 除了登录接口(获取token的接口),所有其他接口都要使用令牌(token)进行权限验证。
token使用方式目前统一为:
- 若为GET或DELETE方法接口,token加密后作为query参数拼接在url上,参数名为token。
- 若为POST方法接口,并且请求体是json格式的,若无特别说明,都保护一个token属性,值为加密后的token。
- token的加密方式在登录部分会提及。
3. 绝大部分接口可接受`dmPath`参数。当接口是**GET**方法时,此参数作为查询参数拼接在url上。
当接口是**POST**方法,并且请求体时json格式的,此参数作为一个顶级属性。
参数`dmPath`可用来指定作用域路径。作用域在之后会详细提及。故此参数的作用也将会一同进行阐述。
3. 服务端使用fastjson进行json的解析,
该框架支持循环引用的json序列化,
并且对于json树中的同一个引用,
可能会使用特殊的表示方式。具体见[循环引用](https://github.com/alibaba/fastjson/wiki/%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8)
所以返回的json数据里部分属性可能以引用的形式存在。需要进一步解析。
若使用java,可以用fastjson的[JSONPath](https://github.com/alibaba/fastjson/wiki/JSONPath)来进行解析。
若使用javascript,可以用npm上的一个库[fastjson_ref_resolver](https://www.npmjs.com/package/fastjson_ref_resolver)来进一步处理。
/* eslint-disable no-undef */
import React from 'react';
import { Spin, Tree } from 'antd';
import { connect } from 'dva';
import { Route, Switch, routerRedux } from 'dva/router';
import { makeAsync } from 'react-async-wrapper';
import 'github-markdown-css';
import { withSize } from '../../../../components/hoc/size';
import { userApi } from '../../../../services/interfaces';
import { processError } from '../../../../utils/error';
import styles from './index.less';
import mdIndex from './index.md';
import mdAuth from './auth.md';
import mdDomain from './domain.md';
import mdFile from './file.md';
import mdDyInt from './dynamic-interface/index.md';
import mdError from './error.md';
import createPages from './dynamic-interface';
import md from './markdown';
const TreeNode = Tree.TreeNode;
class DocMainPage extends React.PureComponent {
onSelect = match => (ignored, e) => {
const key = e.node.props.eventKey;
this.props.dispatch(routerRedux.push(`${match.url}/${key}`));
};
render() {
const { match, size, infoes, loading } = this.props;
const pages = loading ? [] : createPages(match, infoes, size);
return (
<Spin spinning={loading} style={{ height: size.height, maxHeight: size.height }}>
<div className={styles.main} style={{ height: size.height }}>
<div className={styles.leftMenu}>
<Tree defaultExpandAll onSelect={this.onSelect(match)}>
<TreeNode title="前置说明" key="index" />
<TreeNode title="登录与认证" key="auth" />
<TreeNode title="作用域" key="domain" />
<TreeNode title="文件管理" key="file" />
<TreeNode title="动态接口" key="interface">
{
pages.map(page => (
<TreeNode title={page.showName} key={page.key} />
))
}
</TreeNode>
<TreeNode title="错误码" key="error" />
</Tree>
</div>
<div className={styles.contents}>
<Switch>
<Route path={`${match.path}/index`} component={md(mdIndex)} />
<Route path={`${match.path}/auth`} component={md(mdAuth)} />
<Route path={`${match.path}/domain`} component={md(mdDomain)} />
<Route path={`${match.path}/file`} component={md(mdFile)} />
<Route path={`${match.path}/interface`} exact component={md(mdDyInt)} />
{
pages.map(page => (
<Route key={page.name} path={page.path} render={page.render} />
))
}
<Route path={`${match.path}/error`} component={md(mdError)} />
</Switch>
</div>
</div>
</Spin>
);
}
}
export default makeAsync({
batch: true,
onError: processError,
asyncProps: {
infoes: async () => userApi.getAllInterfaceInfoes(),
},
})(withSize(connect()(DocMainPage)));
import React from 'react';
import cs from 'classnames';
import ReactMarkdown from 'react-markdown';
import Code from './code';
import styles from './markdown.less';
export default mdString => props => (
<div style={{
padding: '1em',
background: '#fafafa',
}}
>
<ReactMarkdown
{...props}
className={cs({
'markdown-body': true,
[styles.markdown]: true,
})}
source={mdString}
renderers={{
code: Code,
}}
/>
</div>
);
.markdown {
ul, ol {
li + li {
margin-top: 1em;
}
li {
ul, ol {
li + li {
margin-top: 0.25em;
}
}
}
}
}
import { connect } from 'dva';
import { withRouter } from 'dva/router';
import List from './list';
import Detail from './detail';
import route from '../../../../components/hoc/routes';
import model from '../../../../models/main/modules/task';
export default connect(({ task }) => ({ task }))(route({
export default binder => route({
childRoutes: [
{
path: 'list',
name: '列表',
component: withRouter(List, { withRef: true }),
component: binder(model)(({ task }) => ({ task }))(List),
},
{
path: 'detail',
name: '详细',
component: withRouter(Detail, { withRef: true }),
component: binder(model)(({ task }) => ({ task }))(Detail),
},
],
}));
});
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Button } from 'antd';
import { connect } from 'dva';
import TableEx from '../../../../components/table/index';
import config from '../../../../utils/config';
import { thisPush } from '../../../../services/route';
import styles from './list.less';
const columns = [{
title: '流程',
dataIndex: 'pName',
key: 'pName',
filterType: 'text',
}, {
title: '任务',
dataIndex: 'nName',
key: 'nName',
filterType: 'text',
}, {
title: '状态',
dataIndex: 'state',
key: 'state',
filterType: 'enum',
filterEnums: [{
text: '状态1',
value: '状态1',
}, {
text: '状态2',
value: '状态2',
}, {
text: '状态3',
value: '状态3',
}, {
text: '状态4',
value: '状态4',
}, {
text: '状态5',
value: '状态5',
}],
}, {
title: '日期',
dataIndex: 'date',
key: 'date',
filterType: 'date',
render(date) {
return date.format(config.defaultDateTimeFormat);
},
}, {
title: '期限',
dataIndex: 'deadline',
key: 'deadline',
render(deadline) {
const now = moment();
const late = deadline.diff(now, 'days', true);
if (late < 0) {
const style = {
color: '#f04134',
};
return <span style={style}>{ `超时 ${deadline.from(now, true)}` }</span>;
} else if (late < 1) {
const style = {
color: '#ffbf00',
};
return <span style={style}>{ `仅剩 ${deadline.to(now, true)}` }</span>;
} else {
const style = {
color: '#00a854',
};
return <span style={style}>{ `还剩 ${deadline.to(now, true)}` }</span>;
}
},
}];
import DsTable from '../../../../components/table/dstable';
import { push } from '../../../../services/route';
class List extends React.Component {
constructor(props, context) {
super(props, context);
this.loadData = this::this.loadData;
this.getCurrent = this::this.getCurrent;
this.state = {
filters: [],
current: 1,
pageSize: 10,
};
}
componentDidMount() {
this.loadData();
}
getCurrent() {
const { num } = this.props.task;
const pageNum = ((num / this.state.pageSize) | 0) + 1;
return this.state.current > pageNum ? pageNum : this.state.current;
}
loadData() {
const filters0 = this.state.filters
.filter(({ filter }) => !!filter)
.map(({ key, filter }) => ([
`f-${key}`,
filter,
]));
const psz = this.state.pageSize; // eslint-disable-line no-shadow
const pst = (this.state.current - 1) * psz;
this.props.dispatch({ type: 'task/fetchTasks', payload: { pst, psz, filters: filters0 } });
}
render() {
const { list, num } = this.props.task;
const tableProps = {
dataSource: list,
columns,
filters: this.state.filters.map(filter => filter.filter),
loading: this.props.loading.effects['task/fetchTasks'],
pagination: {
current: this.state.current,
total: num,
pageSize: this.state.pageSize,
},
onChange: (pagination) => {
this.setState({
current: pagination.current,
pageSize: pagination.pageSize,
}, () => {
this.loadData();
});
},
onFilter: (filters) => {
this.setState({
filters,
current: 1,
}, () => {
this.loadData();
});
},
};
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Button onClick={() => { thisPush(this, { pathname: '../detail', state: { a: 1, b: 2, c: 3 } }); }}>detail</Button>
<TableEx {...tableProps} />
</div>
<div>
<Button onClick={() => {
push('../detail');
}}
>
Detail
</Button>
<DsTable coordinate={this.props.task.coordinate} />
</div>
);
}
}
List.propTypes = {
task: PropTypes.object.isRequired,
};
export default connect(({ task, loading }) => ({ task, loading }))(List);
export default List;
const Monk = ({ children }) => {
return children;
return children || null;
};
export default Monk;
import { mapKeys, toPairs, isUndefined, isString, partial } from 'lodash';
import request from '../utils/request';
import isString from 'lodash/isString';
import partial from 'lodash/fp/partial';
import request, { makeParams } from '../utils/request';
import post from '../utils/post';
import { normParams } from '../utils/http-helper';
import { split } from '../utils/filter';
import config from '../utils/config';
const parseFilters = filtersIn => (filtersIn || []).filter(({ filter }) => !!filter).map(({ key, filter }) => [
split(key, false).map(value => `f-${value}`).join('|'),
filter,
]);
import { getDomain } from '../utils/auth';
export const datasourceApi = (coordinate) => {
const { containerType, containerName, datasourceName } = isString(coordinate) ? {
containerType: 'global',
moduleName: coordinate,
datasourceName: coordinate,
} : (coordinate || {});
if (containerType === 'global') {
return {
query: partial(calcGlobalDatasource, datasourceName),
count: partial(countGlobalDatasource, datasourceName),
cursor: partial(cursorGlobalDatasource, datasourceName),
meta: partial(getGlobalDatasourceMeta, datasourceName),
update: partial(updateGlobalDatasource, datasourceName),
create: partial(createGlobalDatasource, datasourceName),
remove: partial(removeGlobalDatasource, datasourceName),
validateUpdate: partial(validateUpdateGlobalDatasource, datasourceName),
validateCreate: partial(validateCreateGlobalDatasource, datasourceName),
validateRemove: partial(validateRemoveGlobalDatasource, datasourceName),
query: partial(calcGlobalDatasource, [datasourceName]),
queryMeta: partial(calcGlobalDatasourceMeta, [datasourceName]),
count: partial(countGlobalDatasource, [datasourceName]),
countMeta: partial(countGlobalDatasourceMeta, [datasourceName]),
cursor: partial(cursorGlobalDatasource, [datasourceName]),
cursorMeta: partial(cursorGlobalDatasourceMeta, [datasourceName]),
update: partial(updateGlobalDatasource, [datasourceName]),
updateMeta: partial(updateGlobalDatasourceMeta, [datasourceName]),
create: partial(createGlobalDatasource, [datasourceName]),
createMeta: partial(createGlobalDatasourceMeta, [datasourceName]),
remove: partial(removeGlobalDatasource, [datasourceName]),
removeMeta: partial(removeGlobalDatasourceMeta, [datasourceName]),
meta: partial(getGlobalDatasourceMeta, [datasourceName]),
validateUpdate: partial(validateUpdateGlobalDatasource, [datasourceName]),
validateCreate: partial(validateCreateGlobalDatasource, [datasourceName]),
validateRemove: partial(validateRemoveGlobalDatasource, [datasourceName]),
feature: partial(getGlobalDatasourceFeature, [datasourceName]),
invoke: partial(invokeGlobalDatasource, [datasourceName]),
};
} else if (containerType === 'module') {
return {
query: partial(calcModuleDatasource, containerName, datasourceName),
count: partial(countModuleDatasource, containerName, datasourceName),
cursor: partial(cursorModuleDatasource, containerName, datasourceName),
meta: partial(getModuleDatasourceMeta, containerName, datasourceName),
update: partial(updateModuleDatasource, containerName, datasourceName),
create: partial(createModuleDatasource, containerName, datasourceName),
remove: partial(removeModuleDatasource, containerName, datasourceName),
validateUpdate: partial(validateUpdateModuleDatasource, containerName, datasourceName),
validateCreate: partial(validateCreateModuleDatasource, containerName, datasourceName),
validateRemove: partial(validateRemoveModuleDatasource, containerName, datasourceName),
query: partial(calcModuleDatasource, [containerName, datasourceName]),
count: partial(countModuleDatasource, [containerName, datasourceName]),
cursor: partial(cursorModuleDatasource, [containerName, datasourceName]),
update: partial(updateModuleDatasource, [containerName, datasourceName]),
create: partial(createModuleDatasource, [containerName, datasourceName]),
remove: partial(removeModuleDatasource, [containerName, datasourceName]),
meta: partial(getModuleDatasourceMeta, [containerName, datasourceName]),
queryMeta: partial(calcModuleDatasourceMeta, [containerName, datasourceName]),
countMeta: partial(countModuleDatasourceMeta, [containerName, datasourceName]),
cursorMeta: partial(cursorModuleDatasourceMeta, [containerName, datasourceName]),
updateMeta: partial(updateModuleDatasourceMeta, [containerName, datasourceName]),
createMeta: partial(createModuleDatasourceMeta, [containerName, datasourceName]),
removeMeta: partial(removeModuleDatasourceMeta, [containerName, datasourceName]),
validateUpdate: partial(validateUpdateModuleDatasource, [containerName, datasourceName]),
validateCreate: partial(validateCreateModuleDatasource, [containerName, datasourceName]),
validateRemove: partial(validateRemoveModuleDatasource, [containerName, datasourceName]),
feature: partial(getModuleDatasourceFeature, [containerName, datasourceName]),
invoke: partial(invokeModuleDatasource, [containerName, datasourceName]),
};
} else {
throw new Error(`Unsupported containerType: ${containerType}`);
}
};
const makeQueryParams = ({ pst, psz, filters = [], sortBys = [], sortTypes = [], params = {} }) => {
let stBy = sortBys.join(',');
stBy = stBy || undefined;
let stType = sortTypes.join(',');
stType = stType || undefined;
return [
...toPairs({ pst, psz, stBy, stType }),
...parseFilters(filters),
...normParams(mapKeys(params, (v, k) => `p-${k}`)),
].filter(v => v && !isUndefined((v[1])));
};
const makeParams = (otherParams, { filters = [], sortBys = [], sortTypes = [], params = {} }) => {
return [
...toPairs(otherParams),
...makeQueryParams({ filters, sortBys, sortTypes, params }),
].filter(v => v && !isUndefined((v[1])));
};
export async function calcGlobalDatasource(name, { pst, psz, filters = [], sortBys = [], sortTypes = [], params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/datasource/${name}`, makeParams({ pst, psz, dmPath }, { filters, sortBys, sortTypes, params }));
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}`, makeParams({ pst, psz, dmPath }, { filters, sortBys, sortTypes, params }));
}
export async function calcGlobalDatasourceMeta(name, { params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta/query`, makeParams({ dmPath }, { params }));
}
export async function countGlobalDatasource(name, { filters = [], params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/datasource/${name}/count`, makeParams({ dmPath }, { filters, params }));
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/count`, makeParams({ dmPath }, { filters, params }));
}
export async function countGlobalDatasourceMeta(name, { params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta/count`, makeParams({ dmPath }, { params }));
}
export async function cursorGlobalDatasource(name, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/datasource/${name}/cursor`, makeParams({ key, dmPath }, { params }));
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/cursor`, makeParams({ key, dmPath }, { params }));
}
export async function cursorGlobalDatasourceMeta(name, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta/cursor`, makeParams({ key, dmPath }, { params }));
}
export async function validateUpdateGlobalDatasource(name, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/datasource/${name}/update/validate`, makeParams({ key, dmPath }, { params }));
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/update/validate`, makeParams({ key, dmPath }, { params }));
}
export async function updateGlobalDatasource(name, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/datasource/${name}/update`, {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/update`, {
key,
params,
dmPath,
});
}
export async function updateGlobalDatasourceMeta(name, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta/update`, {
key,
params,
dmPath,
......@@ -90,50 +102,96 @@ export async function updateGlobalDatasource(name, key, params = {}, dmPath) {
}
export async function validateCreateGlobalDatasource(name, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/datasource/${name}/create/validate`, makeParams({ dmPath }, { params }));
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/create/validate`, makeParams({ dmPath }, { params }));
}
export async function createGlobalDatasource(name, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/datasource/${name}/create`, {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/create`, {
params,
dmPath,
});
}
export async function createGlobalDatasourceMeta(name, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta/create`, {
params,
dmPath,
});
}
export async function validateRemoveGlobalDatasource(name, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/datasource/${name}/remove/validate`, makeParams({ key, dmPath }, { params }));
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/remove/validate`, makeParams({ key, dmPath }, { params }));
}
export async function removeGlobalDatasource(name, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/datasource/${name}/remove`, {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/remove`, {
key,
params,
dmPath,
});
}
export async function removeGlobalDatasourceMeta(name, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta/remove`, {
key,
params,
dmPath,
});
}
export async function getGlobalDatasourceMeta(name) {
return request(`${config.apiContextPath}/api/datasource/${name}/meta`);
export async function getGlobalDatasourceMeta(name, dmPath) {
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/meta`, makeParams({ dmPath }, {}));
}
export async function getGlobalDatasourceFeature(name, dmPath) {
return request(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(name)}/feature`, makeParams({ dmPath }, {}));
}
export async function invokeGlobalDatasource(dsName, opName, params = {}, dmPath, { pst, psz, filters = [], includeKeys, excludeKeys, termWhenFail = true }) {
return post(`${config.apiContextPath}/api/datasource/user/${encodeURIComponent(dsName)}/operation/${encodeURIComponent(opName)}/invoke`, {
includeKeys,
excludeKeys,
}, makeParams({ twf: termWhenFail ? 1 : 0, pst, psz }, { filters, params }));
}
export async function calcModuleDatasource(mdName, dsName, { pst, psz, filters = [], sortBys = [], sortTypes = [], params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}`, makeParams({ pst, psz, dmPath }, { filters, sortBys, sortTypes, params }));
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}`, makeParams({ pst, psz, dmPath }, { filters, sortBys, sortTypes, params }));
}
export async function calcModuleDatasourceMeta(mdName, dsName, { params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta/query`, makeParams({ dmPath }, { params }));
}
export async function countModuleDatasource(mdName, dsName, { filters = [], params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/count`, makeParams({ dmPath }, { filters, params }));
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/count`, makeParams({ dmPath }, { filters, params }));
}
export async function countModuleDatasourceMeta(mdName, dsName, { params = {}, dmPath }) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta/count`, makeParams({ dmPath }, { params }));
}
export async function cursorModuleDatasource(mdName, dsName, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/cursor`, makeParams({ key, dmPath }, { params }));
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/cursor`, makeParams({ key, dmPath }, { params }));
}
export async function cursorModuleDatasourceMeta(mdName, dsName, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta/cursor`, makeParams({ key, dmPath }, { params }));
}
export async function validateUpdateModuleDatasource(mdName, dsName, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/update/validate`, makeParams({ key, dmPath }, { params }));
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/update/validate`, makeParams({ key, dmPath }, { params }));
}
export async function updateModuleDatasource(mdName, dsName, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/update`, {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/update`, {
key,
params,
dmPath,
});
}
export async function updateModuleDatasourceMeta(mdName, dsName, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta/update`, {
key,
params,
dmPath,
......@@ -141,29 +199,153 @@ export async function updateModuleDatasource(mdName, dsName, key, params = {}, d
}
export async function validateCreateModuleDatasource(mdName, dsName, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/create/validate`, makeParams({ dmPath }, { params }));
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/create/validate`, makeParams({ dmPath }, { params }));
}
export async function createModuleDatasource(mdName, dsName, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/create`, {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/create`, {
params,
dmPath,
});
}
export async function createModuleDatasourceMeta(mdName, dsName, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta/create`, {
params,
dmPath,
});
}
export async function validateRemoveModuleDatasource(mdName, dsName, key, params = {}, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/remove/validate`, makeParams({ key, dmPath }, { params }));
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/remove/validate`, makeParams({ key, dmPath }, { params }));
}
export async function removeModuleDatasource(mdName, dsName, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/remove`, {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/remove`, {
key,
params,
dmPath,
});
}
export async function removeModuleDatasourceMeta(mdName, dsName, key, params = {}, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta/remove`, {
key,
params,
dmPath,
});
}
export async function getModuleDatasourceMeta(mdName, dsName) {
return request(`${config.apiContextPath}/api/module/user/${mdName}/datasource/${dsName}/meta`);
export async function getModuleDatasourceMeta(mdName, dsName, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/meta`, makeParams({ dmPath }, {}));
}
export async function getModuleDatasourceFeature(mdName, dsName, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/feature`, makeParams({ dmPath }, {}));
}
export async function invokeModuleDatasource(mdName, dsName, opName, params = {}, dmPath, { pst, psz, filters = [], includeKeys, excludeKeys, termWhenFail = true }) {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/datasource/${encodeURIComponent(dsName)}/operation/${encodeURIComponent(opName)}/invoke`, {
includeKeys,
excludeKeys,
}, makeParams({ twf: termWhenFail ? 1 : 0, pst, psz }, { filters, params }));
}
export class Datasource {
constructor(coordinate, dmPath) {
this.api = datasourceApi(coordinate);
this.dmPath = dmPath;
this.dealWithPath = ::this.dealWithPath;
this.query = ::this.query;
this.queryMeta = ::this.queryMeta;
this.count = ::this.count;
this.countMeta = ::this.countMeta;
this.cursor = ::this.cursor;
this.cursorMeta = ::this.cursorMeta;
this.update = ::this.update;
this.updateMeta = ::this.updateMeta;
this.validateUpdate = ::this.validateUpdate;
this.create = ::this.create;
this.createMeta = ::this.createMeta;
this.validateCreate = ::this.validateCreate;
this.remove = ::this.remove;
this.removeMeta = ::this.removeMeta;
this.validateRemove = ::this.validateRemove;
}
async dealWithPath() {
if (this.dmPath) {
return this.dmPath;
} else {
const domain = await getDomain();
return domain.path;
}
}
async query(params = {}, pst = 0, psz = -1, { filters = [], sortBys = [], sortTypes = [] }) {
const dmPath = await this.dealWithPath();
return this.api.query({ pst, psz, filters, sortBys, sortTypes, params, dmPath });
}
async queryMeta(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.query({ params, dmPath });
}
async count(params = {}, { filters = [] }) {
const dmPath = await this.dealWithPath();
return this.api.count({ filters, params, dmPath });
}
async countMeta(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.countMeta({ params, dmPath });
}
async cursor(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.cursor(key, params, dmPath);
}
async cursorMeta(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.cursorMeta(key, params, dmPath);
}
async update(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.update(key, params, dmPath);
}
async updateMeta(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.updateMeta(key, params, dmPath);
}
async validateUpdate(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.validateUpdate(key, params, dmPath);
}
async create(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.create(params, dmPath);
}
async createMeta(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.createMeta(params, dmPath);
}
async validateCreate(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.validateCreate(params, dmPath);
}
async remove(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.remove(key, params, dmPath);
}
async removeMeta(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.removeMeta(key, params, dmPath);
}
async validateRemove(key, params = {}) {
const dmPath = await this.dealWithPath();
return this.api.validateRemove(key, params, dmPath);
}
async feature() {
const dmPath = await this.dealWithPath();
return this.api.feature(dmPath);
}
async invoke(name, params = {}, { pst, psz, filters = [], includeKeys, excludeKeys, termWhenFail = true }) {
const dmPath = await this.dealWithPath();
return this.api.invoke(name, params, dmPath, { pst, psz, filters, includeKeys, excludeKeys, termWhenFail });
}
}
......@@ -2,7 +2,7 @@
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
import { getDomain, histories } from '../utils/auth';
import { getDomain, getUser, histories } from '../utils/auth';
export async function fetchDomains(basePath, withRoot = false) {
if (!basePath) {
......@@ -30,9 +30,14 @@ export async function fetchDomains(basePath, withRoot = false) {
return infoList;
}
export async function getInfo(dmPath) {
return request(`${config.apiContextPath}/api/domain/user/info`, { dmPath });
}
export async function switchDomain(path) {
await post(`${config.apiContextPath}/api/domain/user/path`, { dmPath: path });
histories.pushHistory('domain', path);
const user = await getUser();
histories.pushHistory('domain', user && user.id, path);
}
export async function currentDomain() {
......
import request from '../utils/request';
import isString from 'lodash/isString';
import partial from 'lodash/fp/partial';
import request, { makeParams } from '../utils/request';
import post from '../utils/post';
import doDelete from '../utils/delete';
import config from '../utils/config';
import { getDomain } from '../utils/auth';
export const interfaceApi = (coordinate) => {
const { containerType, containerName, interfaceName } = isString(coordinate) ? {
containerType: 'global',
interfaceName: coordinate,
} : (coordinate || {});
if (containerType === 'global') {
return {
invoke: partial(userApi.invokeInterface, [interfaceName]),
validate: partial(userApi.validateState, [interfaceName]),
};
} else if (containerType === 'module') {
return {
invoke: partial(userApi.invokeModuleInterface, [containerName, interfaceName]),
validate: partial(userApi.validateModuleInterface, [containerName, interfaceName]),
};
} else {
throw new Error(`Unsupported containerType: ${containerType}`);
}
};
export const userApi = {
async getAllInterfaceInfoes() {
......@@ -10,12 +33,21 @@ export const userApi = {
async getInterfaceInfo(name) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/info`);
},
async validateState(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/invoke/validateState`, { dmPath });
async validateState(name, params, dmPath) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/invoke/validateState`, makeParams({ dmPath }, { params }));
},
async invokeInterface(name, params, dmPath) {
return post(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/invoke`, { params, dmPath });
},
async validateModuleInterface(mdName, name, params, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/interface/${encodeURIComponent(name)}/invoke/validateState`, makeParams({ dmPath }, { params }));
},
async invokeModuleInterface(mdName, name, params, dmPath) {
return post(`${config.apiContextPath}/api/module/user/${encodeURIComponent(mdName)}/interface/${encodeURIComponent(name)}/invoke`, { params, dmPath });
},
async getInterfaceDocument(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/document`, { dmPath });
},
};
export const adminApi = {
......@@ -29,12 +61,50 @@ export const adminApi = {
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { dmPath });
},
async getInterfacePlainConfigure(name, dmPath) {
return post(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/plainConfigure`, { dmPath });
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/plainConfigure`, { dmPath });
},
async getInterfacePlainConfigures(dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/plainConfigures`, { dmPath });
},
async setInterfaceConfigure(name, configure, dmPath) {
return post(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { configure, dmPath });
},
async setInterfaceConfigures(configure, dmPath) {
return post(`${config.apiContextPath}/api/interface/admin/configures`, { configure, dmPath });
},
async removeInterfaceConfigure(name, dmPath) {
return doDelete(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { dmPath });
},
async getInterfaceDocument(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/document`, { dmPath });
},
async setInterfaceDocument(name, document, dmPath) {
return post(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/document`, { document, dmPath });
},
};
export class Interface {
constructor(coordinate, dmPath) {
this.api = interfaceApi(coordinate);
this.dmPath = dmPath;
this.dealWithPath = ::this.dealWithPath;
this.invoke = ::this.invoke;
this.validate = ::this.validate;
}
async dealWithPath() {
if (this.dmPath) {
return this.dmPath;
} else {
const domain = await getDomain();
return domain.path;
}
}
async invoke(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.invoke(params, dmPath);
}
async validate(params = {}) {
const dmPath = await this.dealWithPath();
return this.api.validate(params, dmPath);
}
}
/* eslint-disable no-underscore-dangle */
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
import { Datasource } from './datasource';
import { Interface } from './interfaces';
export async function fetchMenus() {
return request(`${config.apiContextPath}/api/configure/user/menus`);
......@@ -10,5 +14,121 @@ export async function fetchModuleInfos() {
}
export async function fetchModuleLayout(name) {
return request(`${config.apiContextPath}/api/module/user/${name}/layout`);
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(name)}/layout`);
}
export const adminApi = {
async allInfoes(dmPath) {
return request(`${config.apiContextPath}/api/module/admin/info`, { dmPath });
},
async getInfo(name, dmPath) {
return request(`${config.apiContextPath}/api/module/admin/${encodeURIComponent(name)}/info`, { dmPath });
},
async getConfigure(name, dmPath) {
return request(`${config.apiContextPath}/api/module/admin/${encodeURIComponent(name)}/configure`, { dmPath });
},
async getPlainConfigure(name, dmPath) {
return request(`${config.apiContextPath}/api/module/admin/${encodeURIComponent(name)}/plainConfigure`, { dmPath });
},
async setConfigure(name, configure, dmPath) {
return post(`${config.apiContextPath}/api/module/admin/${encodeURIComponent(name)}/configure`, { dmPath, configure });
},
async getPlainConfigures(dmPath) {
return request(`${config.apiContextPath}/api/module/admin/plainConfigures`, { dmPath });
},
async setConfigures(configures, dmPath) {
return post(`${config.apiContextPath}/api/module/admin/configures`, { dmPath, configure: configures });
},
async getDocument(name, dmPath) {
return request(`${config.apiContextPath}/api/module/admin/${encodeURIComponent(name)}/document`, { dmPath });
},
async setDocument(name, document, dmPath) {
return post(`${config.apiContextPath}/api/module/admin/${encodeURIComponent(name)}/document`, { dmPath, document });
},
};
export const userApi = {
async allInfoes(dmPath) {
return request(`${config.apiContextPath}/api/module/user/info`, { dmPath });
},
async getInfo(name, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(name)}/info`, { dmPath });
},
async getLayout(name, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(name)}/layout`, { dmPath });
},
async getDocument(name, dmPath) {
return request(`${config.apiContextPath}/api/module/user/${encodeURIComponent(name)}/document`, { dmPath });
},
};
export class Module {
constructor(name, dmPath) {
this.name = name;
this.dmPath = dmPath;
this._admin = false;
}
get admin() {
return this._admin;
}
set admin(admin) {
this._admin = admin;
}
get info() {
if (this.admin) {
return adminApi.getInfo(this.name, this.dmPath);
} else {
return userApi.getInfo(this.name, this.dmPath);
}
}
get layout() {
if (this.admin) {
return adminApi.getLayout(this.name, this.dmPath);
} else {
return userApi.getLayout(this.name, this.dmPath);
}
}
get document() {
if (this.admin) {
return adminApi.getDocument(this.name, this.dmPath);
} else {
return userApi.getDocument(this.name, this.dmPath);
}
}
async setDocument(doc) {
if (this.admin) {
return adminApi.setDocument(this.name, doc, this.dmPath);
}
throw new Error('Set admin to true before set document.');
}
get configure() {
if (this.admin) {
return adminApi.getPlainConfigure(this.name, this.dmPath);
} else {
return userApi.getPlainConfigure(this.name, this.dmPath);
}
}
async setConfigure(conf) {
if (this.admin) {
return adminApi.setConfigure(this.name, conf, this.dmPath);
}
throw new Error('Set admin to true before set configure.');
}
getDatasource(name) {
const coordinate = {
containerType: 'module',
containerName: this.name,
datasourceName: name,
};
return new Datasource(coordinate, this.dmPath);
}
getInterface(name) {
const coordinate = {
containerType: 'module',
containerName: this.name,
interfaceName: name,
};
return new Interface(coordinate, this.dmPath);
}
}
import { castArray } from 'lodash';
import post from '../utils/post';
import doDelete from '../utils/delete';
import config from '../utils/config';
import request from '../utils/request';
import { getToken } from '../utils/auth';
import { encrypt } from '../utils/helper';
import { errors } from '../utils/error';
export class Operations {
constructor() {
......@@ -61,6 +64,100 @@ export class Operations {
};
}
export class Query {
constructor() {
this.pageSize = -1;
}
setAfter = (after) => {
this.after = after;
};
/**
* create_time: 创建时间,
* media_type: 媒体类型,比如application/pdf,
* size: 文件大小,
* name: 文件名称,
* modify_time: 上一次修改时间,
* uri: 资源定位符
*/
setOrder = (...args) => {
const argn = args.length;
if (argn > 0) {
this.orders = [];
let lastOrder;
for (let i = 0; i < argn; ++i) {
if (lastOrder !== undefined && (typeof args[i] === 'boolean')) {
this.orders.push({
target: lastOrder,
asc: args[i],
});
lastOrder = undefined;
} else if (lastOrder !== undefined) {
this.orders.push({
target: lastOrder,
asc: true,
});
lastOrder = args[i];
} else {
lastOrder = args[i];
}
}
if (lastOrder !== undefined) {
this.orders.push({
target: lastOrder,
asc: true,
});
}
}
};
setPageSize = (size) => {
this.pageSize = size >= 0 ? size : -1;
};
setCondition = (condition) => {
this.condition = condition;
};
setStores = (...stores) => {
this.stores = stores;
};
static tag = (...tags) => {
if (tags.length === 0) {
throw new Error('At least one tag!');
}
if (tags.length === 1) {
return {
type: 'tag',
value: tags[0],
};
} else {
return Query.and(...tags.map(tag => Query.tag(tag)));
}
};
static usage = (...usage) => {
if (usage.length === 0) {
throw new Error('At least one usage!');
}
if (usage.length === 1) {
return {
type: 'usage',
value: usage[0],
};
} else {
return Query.and(...usage.map(tag => Query.usage(tag)));
}
};
static and = (...conditions) => {
return {
type: 'and',
conditions,
};
};
static or = (...conditions) => {
return {
type: 'or',
conditions,
};
};
}
export const createOperations = () => {
return new Operations();
};
......@@ -69,38 +166,26 @@ export async function editResource(uri, operations) {
return post(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`, { operations: operations.operations });
}
export async function getResourceInfo(uri) {
return request(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`);
}
export async function queryResourceByUsage(usage, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, pst, psz });
export async function deleteResource(uri) {
return doDelete(await rsLink(uri));
}
export async function queryResourceByUsageStartWith(usage, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, startWith: true, pst, psz });
export async function countResource(query) {
return post(`${config.apiContextPath}/api/resource/user/count`, query);
}
export async function queryResourceByAllTags(tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { tags: castArray(tags), pst, psz });
export async function queryResource(query) {
return post(`${config.apiContextPath}/api/resource/user/query`, query);
}
export async function queryResourceByAnyTags(tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { tags: castArray(tags), andOr: 'or', pst, psz });
}
export async function queryResourceByUsageAndAllTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, tags: castArray(tags), pst, psz });
}
export async function queryResourceByUsageAndAnyTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, tags: castArray(tags), andOr: 'or', pst, psz });
}
export async function queryResourceByUsageStartWithAndAllTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, startWith: true, tags: castArray(tags), pst, psz });
export async function getResourceInfo(uri) {
return request(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`);
}
export async function queryResourceByUsageStartWithAndAnyTags(usage, tags, pst = -1, psz = -1) {
return request(`${config.apiContextPath}/api/resource/user/query`, { usage, startWith: true, tags: castArray(tags), andOr: 'or', pst, psz });
export async function rsLink(uri, download = false) {
const token = await getToken();
if (!token) {
throw errors.tokenMissing();
}
return `${config.apiContextPath}/resource/${encrypt(token)}/${encodeURIComponent(uri)}${download ? '?dl=true' : ''}`;
}
import { browserHistory } from 'dva/router';
import resolvePathname from 'resolve-pathname';
import { isString } from 'lodash';
import config from '../utils/config';
const { contextPath } = config;
const makePath = (base, path, withContext = true) => {
if (path.startsWith('/')) {
return withContext ? `${contextPath}${path}` : path;
}
const basePath = base.endsWith('/') ? base : `${base}/`;
return resolvePathname(path, basePath);
};
const processPath = (base, path, withContext = true) => {
import createBrowserHistory from 'history/createBrowserHistory';
import isString from 'lodash/isString';
import {
push as pushAction,
replace as replaceAction,
go as goAction,
goBack as goBackAction,
goForward as goForwardAction,
} from 'react-router-redux';
import { makePath } from '../utils/helper';
import { getStore } from '../data/app';
const processPath = (base, path, withContext = false) => {
if (isString(path)) {
return makePath(base, path, withContext);
}
......@@ -31,79 +28,67 @@ const getHistoryBase = (history) => {
}
};
export const history = browserHistory;
export const location = {};
let history;
let location;
history.listen((loc) => {
location.pathname = loc.pathname;
location.state = loc.state;
location.search = loc.search;
location.hash = loc.hash;
});
export const getHistory = (...args) => {
return history || createHistory(...args);
};
export const getLocation = () => {
return location || {};
};
export const destroyHistory = () => {
if (history && history.unlisten) {
history.unlisten();
history = null;
}
};
export const createHistory = (...args) => {
destroyHistory();
history = createBrowserHistory(...args);
// noinspection JSUnresolvedFunction
history.unlisten = history.listen((loc) => {
location = { ...loc };
});
return history;
};
export const push = (path, state, withContext = true) => {
return history.push(processPath(getHistoryBase(history), path, withContext), state);
export const push = (path, state) => {
return getStore().dispatch(pushAction(processPath(getHistoryBase(history), path, false), state));
};
export const replace = (path, state, withContext = true) => {
return history.replace(processPath(getHistoryBase(history), path, withContext), state);
export const replace = (path, state) => {
return getStore().dispatch(replaceAction(processPath(getHistoryBase(history), path, false), state));
};
export const go = (n) => {
return history.go(n);
return getStore().dispatch(goAction(n));
};
export const goBack = () => {
return history.goBack();
return getStore().dispatch(goBackAction());
};
export const goForward = () => {
return history.goForward();
};
const checkThis = (theThis) => {
if (!theThis || !theThis.props) {
throw new Error('The this is not a component.');
}
if (!theThis.props.router) {
throw new Error('please use withRouter.');
}
};
const getThisBase = (theThis) => {
if (theThis.props.location) {
return theThis.props.location.pathname;
} else if (typeof window !== 'undefined') {
return window.location.pathname; // eslint-disable-line no-undef
} else {
throw new Error('can not find base path!');
}
return getStore().dispatch(goForwardAction());
};
export const thisPush = (theThis, pathOrLoc, withContext = true) => {
checkThis(theThis);
const route = processPath(getThisBase(theThis), pathOrLoc, withContext);
return theThis.props.router.push(route);
export const thisPush = (theThis, pathOrLoc, state) => {
return push(pathOrLoc, state);
};
export const thisReplace = (theThis, pathOrLoc, withContext = true) => {
checkThis(theThis);
const route = processPath(getThisBase(theThis), pathOrLoc, withContext);
return theThis.props.router.replace(route);
export const thisReplace = (theThis, pathOrLoc, state) => {
return replace(pathOrLoc, state);
};
export const thisGo = (theThis, n) => {
checkThis(theThis);
return theThis.props.router.go(n);
return go(n);
};
export const thisGoBack = (theThis) => {
checkThis(theThis);
return theThis.props.router.goBack();
export const thisGoBack = () => {
return goBack();
};
export const thisGoForward = (theThis) => {
checkThis(theThis);
return theThis.props.router.goForward();
export const thisGoForward = () => {
return goForward();
};
import _, { partial } from 'lodash';
import partial from 'lodash/fp/partial';
import _ from 'lodash/fp/placeholder';
import request from '../utils/request';
import post from '../utils/post';
import doDelete from '../utils/delete';
......@@ -8,15 +9,15 @@ import config from '../utils/config';
export const templateApi = (dmPath) => {
return {
admin: {
getInfo: partial(getAdminTemplateInfo, _, dmPath),
getAllInfoes: partial(getAllAdminTemplateInfoes, dmPath),
getConfigure: partial(getAdminTemplateConfigure, _, dmPath),
getPlainConfigure: partial(getAdminTemplatePlainConfigure, _, dmPath),
setConfigure: partial(setAdminTemplateConfigure, _, _, dmPath),
removeConfigure: partial(removeAdminTemplateConfigure, _, dmPath),
getTemplate: partial(getAdminTemplateTemplate, _, dmPath),
setTemplate: partial(setAdminTemplateTemplate, _, _, dmPath),
render: partial(adminRender, _, _, _, dmPath),
getInfo: partial(getAdminTemplateInfo, [_, dmPath]),
getAllInfoes: partial(getAllAdminTemplateInfoes, [dmPath]),
getConfigure: partial(getAdminTemplateConfigure, [_, dmPath]),
getPlainConfigure: partial(getAdminTemplatePlainConfigure, [_, dmPath]),
setConfigure: partial(setAdminTemplateConfigure, [_, _, dmPath]),
removeConfigure: partial(removeAdminTemplateConfigure, [_, dmPath]),
getTemplate: partial(getAdminTemplateTemplate, [_, dmPath]),
setTemplate: partial(setAdminTemplateTemplate, [_, _, dmPath]),
render: partial(adminRender, [_, _, _, dmPath]),
},
};
};
......
import request from '../../utils/request';
import post from '../../utils/post';
import config from '../../utils/config';
export const auditLegacyEntities = async () => {
return post(`${config.apiContextPath}/api/tools/envers/audit-legacy-entities`);
};
export const getEnversTaskStatus = async () => {
return request(`${config.apiContextPath}/api/tools/envers/audit-legacy-entities/status`);
};
import { partial } from 'lodash';
import partial from 'lodash/partial';
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
......
import { makeAsync as _makeAsync } from 'react-async-wrapper';
import { processError } from './error';
export const makeAsync = (opt, ...args) => _makeAsync({
onError: processError,
...opt,
}, ...args);
......@@ -2,54 +2,137 @@
/**
* Created by yaohx_169 on 2017/6/8.
*/
import { cookie } from './config';
import config, { cookie } from './config';
import { getCookie, setCookie, delCookie } from './helper';
import db from './db';
const getSubValue = (key, subKey) => {
const value = getCookie(key);
if (!value) {
return null;
}
try {
const json = JSON.parse(value);
return json[subKey];
} catch (e) {
delCookie(key);
throw new Error(`损坏的${key} cookie!`);
}
};
const setSubValue = (key, subKey, value) => {
const v = getCookie(key);
if (v) {
try {
const json = JSON.parse(v);
json[subKey] = value;
setCookie(key, JSON.stringify(json));
} catch (e) {
delCookie(key);
throw new Error(`损坏的${key} cookie!`);
}
} else {
setCookie(key, JSON.stringify({
[subKey]: value,
}));
}
};
const delSubValue = (key, subKey) => {
const value = getCookie(key);
if (value) {
try {
const json = JSON.parse(value);
delete json[subKey];
setCookie(key, JSON.stringify(json));
} catch (e) {
delCookie(key);
throw new Error(`损坏的${key} cookie!`);
}
}
};
const getProductValue = (key) => {
return getSubValue(key, config.productId);
};
const setProductValue = (key, value) => {
setSubValue(key, config.productId, value);
};
const delProductValue = (key) => {
delSubValue(key, config.productId);
};
const getUserValue = (key) => {
const uid = getProductValue(cookie.userId);
if (uid) {
return getSubValue(key, `${config.productId}/${uid}`);
} else {
return null;
}
};
const setUserValue = (key, value) => {
const uid = getProductValue(cookie.userId);
if (uid) {
setSubValue(key, `${config.productId}/${uid}`, value);
}
};
const delUserValue = (key) => {
const uid = getProductValue(cookie.userId);
if (uid) {
delSubValue(key, `${config.productId}/${uid}`);
}
};
export async function getToken() {
return getCookie(cookie.token);
return getProductValue(cookie.token);
}
export async function setToken(token) {
setCookie(cookie.token, token);
setProductValue(cookie.token, token);
}
export async function delToken() {
delCookie(cookie.token);
delProductValue(cookie.token);
}
export async function getUser() {
return {
id: getCookie(cookie.userId),
name: getCookie(cookie.userName),
};
const id = getProductValue(cookie.userId);
return id ? {
id,
name: getProductValue(cookie.userName),
} : null;
}
export async function setUser(id, name) {
setCookie(cookie.userId, id);
setCookie(cookie.userName, name);
setProductValue(cookie.userId, id);
setProductValue(cookie.userName, name);
}
export async function delUser() {
delCookie(cookie.userId);
delCookie(cookie.userName);
delProductValue(cookie.userId);
delProductValue(cookie.userName);
}
export async function getDomain() {
return {
name: getCookie(cookie.domainName),
path: getCookie(cookie.domainPath),
};
const path = getUserValue(cookie.domainPath);
return path ? {
name: getUserValue(cookie.domainName),
path,
} : null;
}
export async function setDomain(name, path) {
setCookie(cookie.domainName, name);
setCookie(cookie.domainPath, path);
setUserValue(cookie.domainName, name);
setUserValue(cookie.domainPath, path);
}
export async function delDomain() {
delCookie(cookie.domainName);
delCookie(cookie.domainPath);
delUserValue(cookie.domainName);
delUserValue(cookie.domainPath);
}
export async function isAuthed() {
......@@ -60,7 +143,7 @@ export async function hasDomain() {
return getDomain().then(result => !!result);
}
const normHistory = (history, size) => {
const normHistory = size => (history) => {
if (!history) {
history = {};
}
......@@ -85,6 +168,24 @@ const normHistory = (history, size) => {
return history;
};
const normData = key => (history) => {
if (!key) {
return history;
}
if (!history[key]) {
history[key] = {};
}
if (!history[key].data) {
history[key].data = [];
history[key].start = 0;
history[key].top = 0;
history[key].empty = true;
}
return history;
};
const selectData = key => history => (key ? history[key] : history);
const next = (i, size) => {
if (i < 0 || i >= size) {
throw new Error(`out of range: ${i} in ${size}`);
......@@ -108,59 +209,65 @@ const prev = (i, size) => {
};
export const histories = {
async getLatest(name) {
let history = await db.get(`history.${name}`).value();
history = normHistory(history);
if (history.empty) {
async getLatest(name, uid) {
return db([config.productId, 'history', name])(normHistory(), normData(uid), (history) => {
const target = selectData(uid)(history);
if (target.empty) {
return null;
}
return history.data[prev(history.top, history.size)];
return target.data[prev(target.top, history.size)];
});
},
async createHistory(name, size) {
let history = db.get(`history.${name}`).value();
history = normHistory(history, size);
return db.set(`history.${name}`, history).write();
async createHistory(name, uid, size) {
return db([config.productId, 'history', name]).write(normHistory(size), normData(uid));
},
async destroyHistory(name) {
return db.unset(`history.${name}`).write();
async destroyHistory(name, uid) {
const path = [config.productId, 'history', name];
if (uid) {
path.push(uid);
}
return db(path).delete();
},
async getHistory(name, size) {
let history = db.get(`history.${name}`).value();
history = normHistory(history, size);
if (history.empty) {
async getHistory(name, uid, size) {
return db([config.productId, 'history', name])(normHistory(size), normData(uid), (h) => {
const target = selectData(uid)(h);
if (target.empty) {
return [];
} else if (history.top > history.start) {
return history.data.slice(history.start, history.top);
} else if (target.top > target.start) {
return target.data.slice(target.start, target.top);
} else {
return [...history.data.slice(history.start, history.size), ...history.data.slice(0, history.top)];
return [...target.data.slice(target.start, h.size), ...target.data.slice(0, target.top)];
}
});
},
async pushHistory(name, value, size) {
let history = await db.get(`history.${name}`).value();
history = normHistory(history, size);
history.data[history.top] = value;
const nextPos = next(history.top, history.size);
if (!history.empty && history.start === history.top) {
history.top = history.start = nextPos;
async pushHistory(name, uid, value, size) {
return db([config.productId, 'history', name]).write(normHistory(size), normData(uid), (history) => {
const target = selectData(uid)(history);
target.data[target.top] = value;
const nextPos = next(target.top, history.size);
if (!target.empty && target.start === target.top) {
target.top = target.start = nextPos;
} else {
history.top = nextPos;
target.top = nextPos;
}
if (history.empty) {
history.empty = false;
if (target.empty) {
target.empty = false;
}
return db.set(`history.${name}`, history).write();
return history;
});
},
async popHistory(name) {
let history = await db.get(`history.${name}`).value();
history = normHistory(history);
if (history.empty) {
async popHistory(name, uid) {
return db([config.productId, 'history', name]).write(normHistory(), normData(uid), (history) => {
const target = selectData(uid)(history);
if (target.empty) {
return;
}
history.top = prev(history.top, history.size);
if (history.top === history.start) {
history.empty = true;
target.top = prev(target.top, history.size);
if (target.top === target.start) {
target.empty = true;
}
return db.set(`history.${name}`, history).write();
return history;
});
},
async init() {
return db.read();
......
import baseConfig from '../../config';
/* eslint-disable no-undef */
/**
* Created by yaohx_169 on 2017/6/6.
......@@ -48,17 +50,19 @@ const defaultDateFormat = 'YYYY-MM-DD';
const defaultTimeFormat = 'HH:mm:ss';
const defaultDateTimeFormat = `${defaultDateFormat} ${defaultTimeFormat}`;
// eslint-disable-next-line no-underscore-dangle
const _apiContextPath = process.env.NODE_ENV === 'development' ? '' : apiContextPath;
// eslint-disable-next-line no-underscore-dangle
const _contextPath = process.env.NODE_ENV === 'development' ? '' : contextPath;
const dev = process.env.NODE_ENV === 'development';
const contextPath = dev ? baseConfig.dev.contextPath : baseConfig.prod.contextPath;
const apiContextPath = dev ? baseConfig.dev.apiContextPath : baseConfig.prod.apiContextPath;
const basename = dev ? baseConfig.dev.basename : baseConfig.prod.basename;
const config = {
name: 'Jbpm Demo',
footerText: '上海铂蓝信息科技有限公司',
logo: `${_contextPath}/logo.png`,
contextPath: _contextPath,
apiContextPath: _apiContextPath,
logo: `${contextPath}/logo.png`,
basename,
contextPath,
apiContextPath,
productId: 'big-machine-web-front',
fastNavigationPage: '',
defaultDateFormat,
......
import toPlainObject from 'lodash/toPlainObject';
export const parseQueryResult = ({ dataType, arrayData, singularData }, meta) => {
if (dataType === 'TABLE') {
const data = (arrayData || []).map(() => ({}));
(meta.properties || [])
.filter(property => !property.skip)
.forEach((property, i) => {
data.forEach((row, j) => {
row[property.name] = arrayData[j][i]; // eslint-disable-line no-param-reassign
});
});
return data;
} else if (dataType === 'PROPERTIES') {
const data = [];
(meta.properties || [])
.filter(property => !property.skip)
.forEach((property) => {
data.push((singularData || {})[property.name]);
});
return [toPlainObject(data)];
} else {
throw new Error(`Unsupported data type: ${dataType}`);
}
};
/* eslint-disable no-param-reassign */
/** @module utils/db */
import low from 'lowdb';
import set from 'lodash/set';
import unset from 'lodash/unset';
import flow from 'lodash/fp/flow';
import getOr from 'lodash/fp/getOr';
import LocalStorage from 'lowdb/adapters/LocalStorage';
import { isPromise } from './helper';
const adapter = new LocalStorage('db');
// eslint-disable-next-line no-underscore-dangle
const _adapter_ = new LocalStorage('db');
/**
* @typedef {Object} DB
* @template {T}
* @param {CreateDB} db
* @param key
* @param adapter
* @return {*}
*/
const init = function init(db, key, adapter) {
db.read = () => {
const r = adapter.read();
return isPromise(r) ? r.then(db.plant) : db.plant(r);
};
db.write = (...args) => {
const value = args.length > 0 && args[0] !== undefined ? args[0] : db.getState();
const w = adapter.write(db.getState());
return isPromise(w) ? w.then(() => {
return value;
}) : value;
};
db.plant = (state) => {
db[key] = state;
return db;
};
db.getState = () => {
return db[key];
};
db.setState = (state) => {
db.plant(state);
return db;
};
return db.read();
};
/**
* @member {Function} DB~get
* @param {Array.<string>|string} path
* @typedef {Function} CreateDB
* @param {string} path
* @param {*} [defaultValue]
* @return {DB.<*>}
* @return {DB}
*/
/**
* @member {Function} DB~set
* @param {Array.<string>|string} path
* @param value
* @return {DB}
* @member {Function} CreateDB~read
* @return {*|Promise.<*>}
*/
/**
* @member {Function} DB~find
* @template {T}
* @param {Function} [predicate=_.identity]
* @param {number} [fromIndex=0]
* @return {DB.<T>}
* @member {Function} CreateDB~write
* @param {*} [state]
* @return {*|Promise.<*>}
*/
/**
* @member {Function} DB~unset
* @param {Array.<string>|string} path
* @return {DB.<boolean>}
* @member {Function} CreateDB~plant
* @param {*} state
* @return {CreateDB}
*/
/**
* @member {Function} DB~read
* @return {Promise.<*>}
* @member {Function} CreateDB~getState
* @return {*}
*/
/**
* @member {Function} CreateDB~setState
* @param {*} state
* @return {CreateDB}
*/
/**
* @typedef {Function} DB
* @param {...Function} functions
* @return {*}
*/
/**
* @member {Function} DB~write
* @return {Promise.<*>}
* @param {...Function} functions
* @return {*|Promise.<*>}
*/
/**
* @member {Function} DB~value
* @template {T}
* @return {Promise.<T>}
* @member {Function} DB~delete
* @return {*|Promise.<*>}
*/
/**
* @type {DB.<*>}
* @return {CreateDB}
*/
const db = low(adapter);
const CreateDB = () => {
function db(path, defaultValue) {
function getValue(...functions) {
const result = getOr(defaultValue, path, db.getState());
return flow(...functions)(result);
}
getValue.write = (...args) => {
const result = getValue(...args);
set(db.getState(), path, result);
return db.write();
};
getValue.delete = (flush = true) => {
unset(db.getState(), path);
return flush ? db.write() : db.getState();
};
return getValue;
}
export default db;
return init(db, '__state__', _adapter_);
};
export default CreateDB();
/* eslint-disable no-param-reassign */
import { isNil, defaults } from 'lodash';
import isNil from 'lodash/isNil';
import defaults from 'lodash/defaults';
import { fetch } from './polyfill';
import { checkStatus, normParams, parseObject } from './http-helper';
import middleware from './middleware';
......
import React from 'react';
import { message, Modal } from 'antd';
import debounce from 'lodash/debounce';
import memoize from 'lodash/memoize';
import { push } from '../services/route';
import { errors as errorCodes } from './config';
......@@ -12,18 +14,40 @@ const errStyle = {
marginTop: '24px',
};
function memDebounce(func, wait = 0, options = {}) {
const mem = memoize(() => {
const memFunc = (...args) => {
const ret = func(...args);
const { cache } = mem;
cache.delete(args[0]);
return ret;
};
// eslint-disable-next-line lodash-fp/no-extraneous-args
return debounce(memFunc, wait, options);
});
return (...args) => mem(...args)(...args);
}
const msgError = memDebounce((msg) => {
message.error(msg);
}, 300);
export function processError(err) {
if (err) {
if (err.data && err.data.errorCode) {
const data = err.data;
switch (data.errorCode) {
case errorCodes.no_such_user:
message.error('用户不存在!');
msgError('用户不存在!');
break;
case errorCodes.invalid_token:
case errorCodes.token_missing:
push('/login');
break;
case errorCodes.no_domain_right:
msgError('没有此作用域权限。');
push('/domain');
break;
default:
showError(err);
}
......@@ -44,7 +68,7 @@ function showError(err) {
msg = err.message;
}
if (msg && msg.length < 256) {
message.error(msg);
msgError(msg);
} else {
Modal.error({
title: '服务器内部错误',
......
/* eslint-disable no-param-reassign */
import moment from 'moment';
import _ from 'lodash';
import pickBy from 'lodash/pickBy';
import negate from 'lodash/negate';
import isUndefined from 'lodash/isUndefined';
import resolvePathname from 'resolve-pathname';
import { createJSEncrypt } from './jsencrypt';
import config from './config';
const { contextPath, pubKey } = config;
const toKey = (key) => {
let ret = key.replace(/(_{2,})/g, '$1_');
ret = ret.replace(/-/g, '__');
if (!/^\w+$/.test(ret)) {
throw new Error(`Invalid cookie key: ${key}.`);
}
return ret;
};
export function setCookie(name, value, options = {}) {
const { path, domain, expires } = options;
name = toKey(name);
if (name) {
const expireSet = expires ? ` expires=${moment().add(expires, 'ms').toDate().toUTCString()};` : '';
const domainSet = domain ? ` domain=${domain};` : '';
const pathSet = path ? ` path=${path};` : '';
const pathSet = path ? ` path=${path};` : ' path=/;';
const valueSet = value ? `${name}=${encodeURIComponent(value)};` : '';
document.cookie = `${valueSet}${expireSet}${domainSet};${pathSet}`; // eslint-disable-line
document.cookie = `${valueSet}${expireSet}${domainSet}${pathSet}`; // eslint-disable-line
}
}
export function getCookie(name) {
name = toKey(name);
const reg = new RegExp(`(^|)${name}=([^;]*)(;|$)`, 'g');
const arr = document.cookie.match(reg); // eslint-disable-line
if (arr) {
......@@ -32,8 +47,9 @@ export function getCookie(name) {
export function delCookie(name, { domain, path } = {}) {
if (getCookie(name)) {
name = toKey(name);
const domainSet = domain ? ` domain=${domain};` : '';
const pathSet = path ? ` path=${path};` : '';
const pathSet = path ? ` path=${path};` : ' path=/';
document.cookie = `${name}=; expires=Thu, 01-Jan-70 00:00:01 GMT;${pathSet}${domainSet}`; // eslint-disable-line
}
}
......@@ -54,25 +70,40 @@ export function locationOrigin(withContext = true) {
return `${location.protocol}//${location.hostname}${location.port ? ':' + location.port : ''}${withContext ? contextPath : ''}`; // eslint-disable-line
}
export function currentPath() {
let path = location.pathname; // eslint-disable-line
if (!path) {
path = '/';
export const makeSureEndsWithSlash = (path) => {
if (!path || !path.endsWith('/')) {
return `${path || ''}/`;
} else {
return path;
}
if (path[0] !== '/') {
path = `/${path}`;
};
const makeSureStartsWithSlash = (path) => {
if (!path || !path.startsWith('/')) {
return `/${path || ''}`;
} else {
return path;
}
};
export const makePath = (base, path, withContext = false) => {
if (path.startsWith('/')) {
return withContext ? `${contextPath}${path}` : path;
}
const basePath = makeSureEndsWithSlash(base);
return resolvePathname(path, basePath);
};
export function currentPath() {
let path = location.pathname; // eslint-disable-line
path = makeSureStartsWithSlash(path);
if (path.slice(0, contextPath.length) === contextPath) {
return path.slice(contextPath.length);
return makeSureStartsWithSlash(path.slice(contextPath.length));
} else {
return '/';
return path;
}
}
export function fullPath(path) {
return `${contextPath}${path}`;
}
export function encrypt(text) {
const jsEncrypt = createJSEncrypt();
jsEncrypt.setPublicKey(pubKey);
......@@ -88,7 +119,7 @@ export function encrypt(text) {
*/
export function queryURL(name) {
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
// eslint-disable-next-line no-undef
// eslint-disable-next-line no-undef
const r = window ? window.location.search.substr(1).match(reg) : null;
if (r !== null) return decodeURI(r[2]);
return null;
......@@ -99,10 +130,10 @@ export function padDigits(number, digits) {
}
export function is(obj, type) {
return (type === 'Null' && obj === null) ||
(type === 'Undefined' && obj === void 0) || // eslint-disable-line no-void
(type === 'Number' && isFinite(obj)) ||
Object.prototype.toString.call(obj).slice(8, -1) === type;
return (type === 'Null' && obj === null)
|| (type === 'Undefined' && obj === void 0) // eslint-disable-line no-void
|| (type === 'Number' && Number.isFinite(obj))
|| Object.prototype.toString.call(obj).slice(8, -1) === type;
}
export function makePromise0(thunk) {
......@@ -118,6 +149,62 @@ export function makePromise1(thunk) {
}
export function filterValidParams(params) {
return _.pickBy(params, _.negate(_.isUndefined));
return pickBy(params, negate(isUndefined));
}
export function isPromise(obj) {
return !!obj && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function';
}
export function shallowEqual(o1, o2, excludes = []) {
if (o1 === o2) {
return true;
}
if (typeof o1 !== 'object' || typeof o2 !== 'object' || o1 === null || o2 === null) {
return false;
}
if (Array.isArray(o1) && Array.isArray(o2)) {
const len = o1.length;
if (len !== o2.length) {
return false;
}
for (let i = 0; i < len; ++i) {
if (o1[i] !== o2[i]) {
return false;
}
}
return true;
}
const keys1 = Object.keys(o1);
const keys2 = Object.keys(o2);
if (keys1.length !== keys2.length) {
return false;
}
for (let i = 0; i < keys1.length; ++i) {
const key = keys1[i];
if (!excludes.includes(key) && (!keys2.includes(key) || o1[key] !== o2[key])) {
return false;
}
}
return true;
}
export const mapObject = (obj, mapper) => {
const newObj = {};
for (const key of Object.keys(obj)) {
newObj[key] = mapper(obj[key], key);
}
return newObj;
};
export const arrayJoin = (arr, joined) => {
const newArr = [];
for (let i = 0; i < arr.length; ++i) {
newArr.push(arr[i]);
newArr.push(joined);
}
if (newArr.length > 0) {
newArr.pop();
}
return newArr;
};
import _ from 'lodash';
import isPlainObject from 'lodash/isPlainObject';
import isObjectLike from 'lodash/isObjectLike';
import isArray from 'lodash/isArray';
import isNumber from 'lodash/isNumber';
import isBoolean from 'lodash/isBoolean';
import isNull from 'lodash/isNull';
import isUndefined from 'lodash/isUndefined';
import isNil from 'lodash/isNil';
import map from 'lodash/map';
import mapValues from 'lodash/mapValues';
import curry from 'lodash/curry';
import flow from 'lodash/fp/flow';
import toPairs from 'lodash/fp/toPairs';
import flatMap from 'lodash/fp/flatMap';
import _ from 'lodash/fp/placeholder';
import { Resolver } from 'fastjson_ref_resolver';
export function checkStatus(response) {
......@@ -13,8 +27,11 @@ export function checkStatus(response) {
}
export function normParams(unnormed) {
if (_.isPlainObject(unnormed)) {
return _(unnormed).toPairs().flatMap(([k, v]) => (_.isArray(v) ? v.map(vv => [k, vv]) : [[k, v]])).value();
if (isPlainObject(unnormed)) {
return flow(
toPairs,
flatMap(([k, v]) => (isArray(v) ? v.map(vv => [k, vv]) : [[k, v]])),
)(unnormed);
} else {
return unnormed;
}
......@@ -29,29 +46,29 @@ export function parseObject(response, middleware, { num2str = false, bool2str =
if (contentType) {
const needMap = num2str || bool2str || nul2str || ud2str || nil2str;
const mapStr = (value) => {
if (num2str && _.isNumber(value)) {
if (num2str && isNumber(value)) {
return value.toString();
}
if (bool2str && _.isBoolean(value)) {
if (bool2str && isBoolean(value)) {
return value.toString();
}
if (nul2str && _.isNull(value)) {
if (nul2str && isNull(value)) {
return '';
}
if (ud2str && _.isUndefined(value)) {
if (ud2str && isUndefined(value)) {
return '';
}
if (nil2str && _.isNil(value)) {
if (nil2str && isNil(value)) {
return '';
}
return value;
};
const mapObj = (obj, mapArrFunc) => {
if (_.isArray(obj)) {
if (isArray(obj)) {
return mapArrFunc(obj, mapObj);
}
if (_.isPlainObject(obj)) {
return _.mapValues(obj, (val) => {
if (isPlainObject(obj)) {
return mapValues(obj, (val) => {
const ret = mapStr(val);
return mapObj(ret, mapArrFunc);
});
......@@ -59,23 +76,23 @@ export function parseObject(response, middleware, { num2str = false, bool2str =
return obj;
};
const mapArr = (arr, mapObjFunc) => {
if (_.isPlainObject(arr)) {
if (isPlainObject(arr)) {
return mapObjFunc(arr, mapArr);
}
if (_.isArray(arr)) {
return _.map(arr, (val) => {
if (isArray(arr)) {
return map(arr, (val) => {
const ret = mapStr(val);
return mapArr(ret, mapObjFunc);
});
}
return arr;
};
const mapValue = _.curry(mapObj)(_, mapArr);
const mapValue = curry(mapObj)(_, mapArr);
if (contentType.indexOf('json') !== -1) {
return response.json()
.then((json) => {
let out = json;
if (_.isObjectLike(out)) {
if (isObjectLike(out)) {
out = new Resolver(out).resolve();
}
return middleware ? middleware(out) : out;
......@@ -84,17 +101,14 @@ export function parseObject(response, middleware, { num2str = false, bool2str =
return needMap ? mapValue(data) : data;
});
} else if (contentType.indexOf('xml') !== -1) {
return response.text()
.then((text) => {
return require.ensure([], (require) => {
const { parseString } = require('xml2js');
const options = {};
return JSON.parse(parseString(text, options));
});
return Promise.all([response.text(), import('xml2js')])
.then(([text, xml2js]) => {
const { parseString } = xml2js;
return JSON.parse(parseString(text, {}));
})
.then((json) => {
let out = json;
if (_.isObjectLike(out)) {
if (isObjectLike(out)) {
out = new Resolver(out).resolve();
}
return middleware ? middleware(out) : out;
......
/* eslint-disable dot-notation,no-param-reassign,no-underscore-dangle */
import { makePath } from './helper';
const Empty = ({ children }) => {
return children || null;
};
const parsePageToRoute = (page, componentMapper) => {
const { pages, childRoutes, component, entry, indexRoute, ...route } = page;
if (!component) {
route.component = Empty;
} else {
const comp = componentMapper[component];
if (!comp) {
throw new Error(`Invalid component: ${component}.`);
}
route.component = comp;
}
const thePages = pages || childRoutes;
if (thePages && thePages.length > 0) {
route.childRoutes = thePages.map(p => parsePageToRoute(p, componentMapper));
}
const theIndexRoute = entry || indexRoute;
if (theIndexRoute) {
if (typeof theIndexRoute !== 'object') {
throw new Error('The \'entry\' must be an object with struct: { path: string, dsName: string, params: object }.');
}
const { path: entryPath, ...state } = theIndexRoute;
if (!entryPath || findRoutesByPath(route.childRoutes, entryPath).length === 0) {
throw new Error('The \'entry\' should have a valid path property.');
}
route.indexRoute = {
onEnter(nextState, replace) {
const theState = nextState.state || {};
replace(makePath(nextState.match.url, entryPath), { ...theState, ...state });
},
};
}
return route;
};
const dealWithPath = (path) => {
if (!path || path === '/') {
return [];
}
let noStart = false;
let noEnd = false;
if (path.startsWith('/')) {
noStart = true;
}
if (path.endsWith('/')) {
noEnd = true;
}
if (noStart || noEnd) {
path = path.substring(noStart ? 1 : 0, noEnd ? (path.length - 1) : path.length);
}
return path.split('/');
};
const findRoutesByPath = (routes, path) => {
const parts = dealWithPath(path);
return _findRoutesByPath(routes, parts);
};
const _findRouteByPath = (route, parts) => {
if (!route || parts.length === 0) {
return [];
}
const [current, ...others] = parts;
if (route.path === current) {
if (parts.length > 1) {
if (route.childRoutes) {
const res = _findRoutesByPath(route.childRoutes, others);
if (res.length > 0) {
return [route, ...res];
} else {
return [];
}
} else {
return [];
}
} else {
return [route];
}
}
};
const _findRoutesByPath = (routes, parts) => {
if (!routes) {
return [];
}
for (const route of routes) {
const res = _findRouteByPath(route, parts);
if (res.length > 0) {
return res;
}
}
return [];
};
export const parseLayout = (layout, componentMapper) => {
const route = {};
if (layout['pages'] || layout.childRoutes) {
route.childRoutes = (layout['pages'] || layout.childRoutes).map(page => parsePageToRoute(page, componentMapper));
} else {
throw new Error('No pages is found!');
}
if (layout.entry) {
if (typeof layout.entry !== 'object') {
throw new Error('The \'entry\' must be an object with struct: { path: string, dsName: string, params: object }.');
}
const { path: entryPath, ...state } = layout.entry;
if (!entryPath || findRoutesByPath(route.childRoutes, entryPath).length === 0) {
throw new Error('The \'entry\' should have a valid path property.');
}
route.indexRoute = {
onEnter(nextState, replace) {
replace(makePath(nextState.match.url, entryPath), state);
},
};
} else {
throw new Error('No entry is found!');
}
return route;
};
import _ from 'lodash';
const { isString, get, sortBy } = _;
import isString from 'lodash/isString';
import get from 'lodash/get';
import flow from 'lodash/fp/flow';
import sortBy from 'lodash/fp/sortBy';
import reverse from 'lodash/fp/reverse';
/**
* @callback KeyExtractor
......@@ -74,9 +76,12 @@ export const toHistogram = (array, key, order = 'desc') => {
}
switch (order) {
case 'desc':
return _(res).sortBy(v => v.num).reverse().value();
return flow(
sortBy(v => v.num),
reverse,
)(res);
case 'asc':
return sortBy(res, v => v.num);
return sortBy(v => v.num, res);
default:
throw new Error(`unsupported order: ${order}`);
}
......
import set from 'lodash/set';
import negate from 'lodash/negate';
import isUndefined from 'lodash/isUndefined';
import pickBy from 'lodash/pickBy';
import forEach from 'lodash/forEach';
import toPlainObject from 'lodash/toPlainObject';
import find from 'lodash/fp/find';
import flow from 'lodash/fp/flow';
import filter from 'lodash/fp/filter';
import { mapObject } from './helper';
export const getKeyName = (meta) => {
const keyProperty = flow(
filter(property => !property.skip),
find(property => property.key),
)(meta.properties || []);
return keyProperty ? keyProperty.name : undefined;
};
export const normMetas = (metas) => {
const ret = {};
if (!metas) {
return ret;
}
forEach(metas, (value, key) => {
let finalValue;
try {
finalValue = JSON.parse(value);
} catch (err) {
finalValue = value;
}
set(ret, key, finalValue);
});
return pickBy(ret, negate(isUndefined));
};
export const parseMetas = (datasource) => {
const res = {
global: {
columnData: {},
rowData: {},
},
properties: {},
};
// noinspection JSUnresolvedVariable
if (datasource.metas) {
res.global.columnData = normMetas(datasource.metas);
}
res.properties = {};
(datasource.properties || [])
.filter(property => !property.skip)
.forEach((property) => {
// noinspection JSUnresolvedVariable
if (property.metas) {
const metas = normMetas(property.metas);
for (const key of Object.keys(metas)) {
const trimedKey = key.trim();
if (trimedKey.startsWith('meta:')) {
const pos = trimedKey.indexOf('.');
if (pos === -1) {
const mName = trimedKey.substring(5).trimLeft();
res.global.rowData[mName] = property.name;
} else {
const pName = trimedKey.substring(5, pos).trimLeft();
const mName = trimedKey.substring(pos + 1).trimLeft();
if (!res.properties[pName]) {
res.properties[pName] = {
columnData: {},
rowData: {},
};
}
res.properties[pName].rowData[mName] = property.name;
}
return;
}
}
if (!res.properties[property.name]) {
res.properties[property.name] = {
columnData: {},
rowData: {},
};
res.properties[property.name].columnData = metas;
}
} else {
res.properties[property.name] = {
columnData: {},
rowData: {},
};
}
});
return res;
};
export const parseQueryResult = ({ dataType, arrayData, singularData }, meta) => {
if (dataType === 'TABLE') {
const data = (arrayData || []).map(() => ({}));
(meta.properties || [])
.filter(property => !property.skip)
.forEach((property, i) => {
data.forEach((row, j) => {
row[property.name] = arrayData[j][i]; // eslint-disable-line no-param-reassign
});
});
return data;
} else if (dataType === 'PROPERTIES') {
const data = [];
(meta.properties || [])
.filter(property => !property.skip)
.forEach((property) => {
data.push((singularData || {})[property.name]);
});
return [toPlainObject(data)];
} else {
throw new Error(`Unsupported data type: ${dataType}`);
}
};
const combineMeta = ({ columnData, rowData }, row) => {
return {
...columnData,
...mapObject(rowData, v => row[v]),
};
};
export const calcPropertyMeta = (property, parsedMeta, row) => {
return combineMeta(parsedMeta.properties[property], row);
};
export const calcGlobalMeta = (parsedMeta, row) => {
return combineMeta(parsedMeta.global, row);
};
......@@ -27,8 +27,8 @@ const putTokenToBody = async (url, data, params, options) => {
params,
options,
data: {
...data,
token,
...data,
},
};
};
......@@ -38,7 +38,9 @@ const parseResponse = (response) => {
if (errorCode === 0) {
return data;
} else {
throw new Error(message || data);
const error = new Error(message || data);
error.data = response;
throw error;
}
};
......@@ -47,6 +49,12 @@ export default {
onRequest: async (url, params, options, auth) => {
if (auth) {
return putTokenOnUrl(url, params, options);
} else {
return {
url,
options,
params,
};
}
},
onResponse: parseResponse,
......@@ -55,6 +63,13 @@ export default {
onRequest: async (url, data, params, options, auth) => {
if (auth) {
return putTokenToBody(url, data, params, options);
} else {
return {
url,
params,
options,
data,
};
}
},
onResponse: parseResponse,
......@@ -63,6 +78,12 @@ export default {
onRequest: async (url, params, options, auth) => {
if (auth) {
return putTokenOnUrl(url, params, options);
} else {
return {
url,
options,
params,
};
}
},
onResponse: parseResponse,
......
/* eslint-disable no-underscore-dangle,no-param-reassign */
import { connect } from 'dva';
import { shallowEqual } from './helper';
const registerModel = (app, model) => {
// noinspection JSUnresolvedVariable
if (app._models.filter(m => m.namespace === model.namespace).length === 1) {
// eslint-disable-next-line no-console
console.warn(`model: ${model.namespace} exist! unmodel it and remodeled.`);
app.unmodel(model.namespace);
}
app.model(model);
};
const normLocalState = (preState, state) => {
if (!preState || !state) {
return state;
}
const { loading: preLoading, ...preModels } = preState;
const { loading, ...models } = state;
if (shallowEqual(preModels, models)) {
if (!preLoading && !loading) {
return preState;
}
if (!preLoading || !loading) {
return state;
}
const { global: preGlobal, models: preModelsLoading, effects: preEffectsLoading } = preLoading;
const { global, models: modelsLoading, effects: effectsLoading } = loading;
if ((preGlobal && !global) || (!preGlobal && global)) {
return state;
}
return (shallowEqual(preModelsLoading, modelsLoading) && shallowEqual(preEffectsLoading, effectsLoading)) ? preState : state;
} else {
return state;
}
};
const hackDispatch = (module, dispatch) => action => dispatch({
...action,
type: `${module}/${action.type}`,
});
const hackSagaEffects = (module, sagaEffects) => {
// const put = (action) => {
// const { type } = action;
// return sagaEffects.put({ ...action, type: `${module}/${type}` });
// };
// put.resolve = (action) => {
// const { type } = action;
// return sagaEffects.put.resolve({ ...action, type: `${module}/${type}` });
// };
const select = (selector, ...args) => {
const _selector = (state, ..._args) => {
const keys = Object.keys(state);
const newState = { ...state };
for (const key of keys) {
if (key.startsWith(`${module}/`)) {
newState[key.substring(module.length + 1)] = newState[key];
delete newState[key];
}
}
return selector(newState, ..._args);
};
return sagaEffects.select(_selector, ...args);
};
// const take = (type) => {
// const { take: oTake } = sagaEffects;
// if (typeof type === 'string') {
// return oTake(`${module}/${type}`);
// } else if (Array.isArray(type)) {
// return oTake(type.map((t) => {
// if (typeof t === 'string') {
// return `${module}/${type}`;
// }
// return t;
// }));
// } else {
// return oTake(type);
// }
// };
return { ...sagaEffects, select };
};
const hackEffect = (module, effect) => function * effectGenerator(action, sagaEffects) {
return yield effect(action, hackSagaEffects(module, sagaEffects));
};
const hackEffects = (module, effects) => {
const hackedEffects = {};
for (const key of Object.keys(effects)) {
const effect = effects[key];
if (Array.isArray(effect)) {
hackedEffects[key] = [hackEffect(module, effect[0]), effect[1]];
} else {
hackedEffects[key] = hackEffect(module, effect);
}
}
return hackedEffects;
};
const hackSubscriptions = (module, subscriptions) => {
const hackedSubscriptions = {};
for (const key of Object.keys(subscriptions)) {
const subscription = subscriptions[key];
hackedSubscriptions[key] = (api, done) => subscription(
{ ...api, dispatch: hackDispatch(module, api.dispatch) },
done,
);
}
return hackedSubscriptions;
};
export const hackModel = (module, model) => {
model = { ...model };
model.namespace = `${module}/${model.namespace}`;
model.initialState = { ...model.state };
model.reducers = model.reducers || {};
model.reducers['@@reset'] = () => model.initialState;
model.effects = hackEffects(module, model.effects || {});
model.subscriptions = hackSubscriptions(module, model.subscriptions || {});
return model;
};
export const bindModel = (app, info, layout) => (...models) => {
const _models = [];
for (let model of models) {
if (typeof model === 'function') {
model = hackModel(info.name, model(info, layout));
} else {
model = hackModel(info.name, model);
}
_models.push(model);
registerModel(app, model);
}
const getLocalNamespace = ns => ns.substring(info.name.length + 1);
return (mapStateToProps, mapDispatchToProps, mergeProps) => (route) => {
let preLocalState = {};
const createLocalState = (state) => {
const localState = {};
for (const model of _models) {
const localNamespace = getLocalNamespace(model.namespace);
localState[localNamespace] = state[model.namespace];
if (state.loading) {
localState.loading = {};
if (state.loading.models) {
localState.loading.models = {};
localState.loading.models[localNamespace] = state.loading.models[model.namespace];
}
if (state.loading.effects) {
localState.loading.effects = {};
const effects = state.loading.effects;
for (const key of Object.keys(effects)) {
if (key.startsWith(`${model.namespace}/`)) {
localState.loading.effects[getLocalNamespace(key)] = state.loading.effects[key];
}
}
localState.loading.global = Object.values(localState.loading.models).some(e => e)
|| Object.values(localState.loading.effects).some(e => e);
}
}
}
return localState;
};
if (!mapStateToProps) {
mapStateToProps = localState => localState;
}
const _mapStateToProps = (state, ownProps) => {
preLocalState = normLocalState(preLocalState, createLocalState(state));
return mapStateToProps(preLocalState, state, ownProps);
};
if (!mapDispatchToProps) {
mapDispatchToProps = dispatch => ({ dispatch });
}
const _mapDispatchToProps = (dispatch, ownProps) => mapDispatchToProps(action => dispatch({
...action,
type: `${info.name}/${action.type}`,
}), ownProps);
return connect(_mapStateToProps, _mapDispatchToProps, mergeProps)(route);
};
};
/* eslint-disable */
export const fetch = require('dva/fetch');
/* eslint-disable no-param-reassign */
import { isNil, defaults } from 'lodash';
import isNil from 'lodash/isNil';
import defaults from 'lodash/defaults';
import { fetch } from './polyfill';
import { checkStatus, normParams, parseObject } from './http-helper';
import middleware from './middleware';
......@@ -10,6 +11,26 @@ const defaultOptions = {
},
};
const sortBody = (keys) => {
const idxToken = keys.indexOf('token');
let tokens = [];
let dmPaths = [];
if (idxToken !== -1) {
tokens = keys.splice(idxToken, 1);
}
const idxDmPath = keys.indexOf('dmPath');
if (idxDmPath !== -1) {
dmPaths = keys.splice(idxDmPath, 1);
}
return [...tokens, ...dmPaths, ...keys];
};
const orderedStringify = (obj) => {
const allKeys = [];
JSON.stringify(obj, (k, v) => { allKeys.push(k); return v; });
return JSON.stringify(obj, sortBody(allKeys));
};
export default async function post(url, data, params = {}, options = {}, auth = true) {
if (!data) {
data = {};
......@@ -31,7 +52,8 @@ export default async function post(url, data, params = {}, options = {}, auth =
}
realOptions.headers['Content-Type'] = 'application/json';
realOptions.method = 'POST';
realOptions.body = JSON.stringify(res ? res.data : data);
const body = res ? res.data : data;
realOptions.body = orderedStringify(body);
return fetch(realUrl, realOptions)
.then(checkStatus)
.then(resp => parseObject(resp, middleware.post.onResponse));
......
import { isNil, defaults } from 'lodash';
import isNil from 'lodash/isNil';
import defaults from 'lodash/defaults';
import toPairs from 'lodash/toPairs';
import mapKeys from 'lodash/mapKeys';
import isUndefined from 'lodash/isUndefined';
import { fetch } from './polyfill';
import { checkStatus, normParams, parseObject } from './http-helper';
import middleware from './middleware';
import { split } from './filter';
const parseFilters = filtersIn => (filtersIn || []).filter(({ filter }) => !!filter).map(({ key, filter }) => [
split(key, false).map(value => `f-${value}`).join('|'),
filter,
]);
const makeQueryParams = ({ pst, psz, filters = [], sortBys = [], sortTypes = [], params = {} }) => {
let stBy = sortBys.join(',');
stBy = stBy || undefined;
let stType = sortTypes.join(',');
stType = stType || undefined;
return [
...toPairs({ pst, psz, stBy, stType }),
...parseFilters(filters),
...normParams(mapKeys(params, (v, k) => `p-${k}`)),
].filter(v => v && !isUndefined((v[1])));
};
export const makeParams = (otherParams, { filters = [], sortBys = [], sortTypes = [], params = {} }) => {
return [
...toPairs(otherParams),
...makeQueryParams({ filters, sortBys, sortTypes, params }),
].filter(v => v && !isUndefined((v[1])));
};
const defaultOptions = {
headers: { Accept: 'application/json' },
......
/* eslint-disable no-console */
import React from 'react';
import { partial } from 'lodash';
import partial from 'lodash/fp/partial';
import { storiesOf } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import ReactMardDown from 'react-markdown';
import ReactJson from 'react-json-view';
import { DatePicker, Form, Input, Modal, Button } from 'antd';
import dva from 'dva';
import createLoading from 'dva-loading';
import DsTable from '../src/components/table/dstable';
import TableInput from '../src/components/table/input-table';
import UCA from '../src/components/uca';
import { makeCancelable } from '../src/utils/promise';
import { login } from '../src/services/login';
import { validate } from '../src/services/login/password';
import { switchDomain } from '../src/services/domain';
import { setToken } from '../src/utils/auth';
const loginIt = async (userName, password, domainPath) => {
const result = await login({ type: 'userName', data: userName, authRequest: await validate(password) });
const { tokenId } = result;
await setToken(tokenId);
await switchDomain(domainPath);
};
const wrap = () => Comp => (props) => {
const app = dva({});
app.use(createLoading({
effects: true,
}));
const { children, ...rest } = props;
app.router(() => <Comp app={app} {...rest}>{ children }</Comp>);
return app.start()();
};
const lazy = action => (Comp) => {
class Lazy extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
loading: true,
};
}
componentDidMount() {
this.action = makeCancelable(action());
this.action.run(promise => promise.then(() => {
this.setState({
loading: false,
});
}));
}
componentWillUnmount() {
this.action.cancel();
}
render() {
if (this.state.loading) {
return <div>加载中...</div>;
} else {
const { children, ...rest } = this.props;
return (
<Comp {...rest}>
{ children }
</Comp>
);
}
}
}
return Lazy;
};
import dva from './lib/dva';
import lazy from './lib/lazy';
import loginIt from './lib/login';
import testMd from './test.md';
storiesOf('a', module)
.add('1', () => {
const Comp = lazy(partial(loginIt, 'admin', 'admin', '/'))(wrap()(DsTable));
const Comp = lazy(partial(loginIt, ['admin', 'admin', '/']))(dva()(DsTable));
const coordinate = {
containerType: 'module',
containerName: 'test',
......@@ -84,12 +30,12 @@ storiesOf('a', module)
const RangePicker = DatePicker.RangePicker;
storiesOf('antd', module)
.add('RangePicker', () => {
.add('RangePicker', withInfo({ inline: true })(() => {
return <RangePicker />;
})
.add('uca', () => {
}))
.add('uca', withInfo({ inline: true })(() => {
return <UCA onChange={evt => console.log(evt)} />;
});
}));
storiesOf('table-input', module)
.add('1', () => {
......@@ -136,3 +82,6 @@ storiesOf('table-input', module)
const Example = Form.create()(Temp);
return <Example />;
});
storiesOf('markdown', module)
.add('test', () => (<ReactMardDown source={testMd} />));
import createLoading from 'dva-loading';
import dva from 'dva';
import React from 'react';
export default function (opt) {
return Comp => (props) => {
const app = dva(opt || {});
app.use(createLoading({
effects: true,
}));
const { children, ...rest } = props;
app.router(() => <Comp app={app} {...rest}>{ children }</Comp>);
return app.start()();
};
}
import React from 'react';
import { makeCancelable } from '../../src/utils/promise';
export default function (action) {
return (Comp) => {
class Lazy extends React.Component {
constructor() {
super();
this.state = {
loading: true,
};
}
componentDidMount() {
this.action = makeCancelable(action());
this.action.run(promise => promise.then(() => {
this.setState({
loading: false,
});
}));
}
componentWillUnmount() {
this.action.cancel();
}
render() {
if (this.state.loading) {
return <div>加载中...</div>;
} else {
const { children, ...rest } = this.props;
return (
<Comp {...rest}>
{ children }
</Comp>
);
}
}
}
return Lazy;
};
}
import { authorize, login } from '../../src/services/login';
import { switchDomain } from '../../src/services/domain';
import { encrypt } from '../../src/utils/helper';
import { validate } from '../../src/services/login/password';
import { setToken } from '../../src/utils/auth';
export default async function (userName, password, domainPath) {
const { tokenId } = await login({ type: 'userName', data: userName });
const authRequest = await validate(password, encrypt(tokenId));
await authorize(authRequest);
await setToken(tokenId);
if (domainPath) {
await switchDomain(domainPath);
}
}
markdown-loader
===============
markdown-loader for webpack using [marked](https://github.com/chjj/marked).
[![](https://img.shields.io/npm/v/markdown-loader.svg)](https://www.npmjs.com/package/markdown-loader)
[![](https://img.shields.io/npm/dm/markdown-loader.svg)](https://www.npmjs.com/package/markdown-loader)
[![Dependency Status](https://david-dm.org/peerigon/markdown-loader.svg)](https://david-dm.org/peerigon/markdown-loader)
[![Build Status](https://travis-ci.org/peerigon/markdown-loader.svg?branch=master)](https://travis-ci.org/peerigon/markdown-loader)
## Installation
`npm install markdown-loader`
## Usage
Since marked's output is HTML, it's best served in conjunction with the [html-loader](https://github.com/webpack/html-loader).
### Webpack 2
```javascript
{
module: {
rules: [{
test: /\.md$/,
use: [
{
loader: "html-loader"
},
{
loader: "markdown-loader",
options: {
/* your options here */
}
}
]
}]
}
}
```
### Options
Simply pass your marked [options](https://github.com/chjj/marked#options-1) as shown above.
In order to specify [custom renderers](https://github.com/peerigon/markdown-loader/issues/5), simply set the `options.renderer`-option on your webpack options.
```javascript
// webpack.config.js
const marked = require("marked");
const renderer = new marked.Renderer();
return {
module: {
rules: [{
test: /\.md$/,
use: [
{
loader: "html-loader"
},
{
loader: "markdown-loader",
options: {
pedantic: true,
renderer
}
}
]
}]
}
}
```
## License
MIT (http://www.opensource.org/licenses/mit-license.php)
## Sponsors
[<img src="https://assets.peerigon.com/peerigon/logo/peerigon-logo-flat-spinat.png" width="150" />](https://peerigon.com)
const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const jsdom = new JSDOM('<!doctype html><html><body/></html>');
const { window } = jsdom;
window.matchMedia = window.matchMedia || function () {
window.matchMedia = window.matchMedia || function matchMedia() {
return {
matches: false,
addListener() {},
......
module.exports = {
'layout-header-height': '48px',
'layout-header-background': '#fff',
'layout-footer-background': '#fff',
'layout-sider-background': '#404040',
'menu-dark-bg': '#404040',
'layout-header-padding': '0',
};
const createProxy = require('http-proxy-middleware');
const forEach = require('lodash/forEach');
const assert = require('assert');
const parseKey = (key) => {
let method = 'get';
let path = key;
if (key.indexOf(' ') > -1) {
const splited = key.split(' ');
method = splited[0].toLowerCase();
path = splited[1];
}
return { method, path };
};
const makeProxy = (method, path, target) => {
const filter = (pathname, req) => {
return path.test(pathname) && req.method === method.toUpperCase();
};
return createProxy(filter, { target });
};
const createMockHandler = (value) => {
return function mockHandler(...args) {
const res = args[1];
if (typeof value === 'function') {
value(...args);
} else {
res.json(value);
}
};
};
module.exports = (app, mock) => {
forEach(mock, (value, key) => {
const parsedkey = parseKey(key);
assert(
typeof value === 'function' ||
typeof value === 'object' ||
typeof value === 'string',
`mock value of ${key} should be function or object or string, but got ${typeof value}`,
);
if (typeof value === 'string') {
let path = parsedkey.path;
if (/\(.+\)/.test(parsedkey.path)) {
path = new RegExp(`^${parsedkey.path}$`);
}
app.use(
path,
makeProxy(parsedkey.method, path, value),
);
} else {
app[parsedkey.method](
parsedkey.path,
createMockHandler(value),
);
}
});
};
const { declare } = require('@babel/helper-plugin-utils');
const plugins = [
// 'babel-plugin-react-require',
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-optional-catch-binding',
'@babel/plugin-proposal-async-generator-functions',
['@babel/plugin-proposal-decorators', {
legacy: true,
}],
['@babel/proposal-class-properties', {
loose: true,
}],
'@babel/plugin-proposal-export-namespace-from',
'@babel/plugin-proposal-export-default-from',
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining',
['@babel/plugin-proposal-pipeline-operator', {
proposal: 'minimal',
}],
'@babel/plugin-proposal-do-expressions',
'@babel/plugin-proposal-function-bind',
'lodash',
['import', { libraryName: 'antd', libraryDirectory: 'es', style: true }],
];
if (process.env.NODE_ENV === 'production') {
plugins.push(
'dev-expression',
'@babel/plugin-transform-react-constant-elements',
'@babel/plugin-transform-react-inline-elements', // not supported by preact.
'transform-react-remove-prop-types',
);
}
if (process.env.NODE_ENV === 'development') {
plugins.push(
'dva-hmr',
);
}
if (process.env.NODE_ENV !== 'test') {
plugins.push(
'@babel/plugin-transform-runtime',
);
}
module.exports = declare(({ assertVersion }, options) => {
assertVersion(7);
return {
presets: [
[
'@babel/env',
{
targets: {
browsers: ['ie >= 9'],
},
modules: options.modules || false,
loose: true,
},
],
'@babel/react',
],
plugins,
};
});
/* eslint-disable no-param-reassign,global-require */
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { existsSync } = require('fs');
const theme = require('./theme');
let hasSassLoader = true;
try {
require.resolve('sass-loader');
} catch (e) {
hasSassLoader = false;
}
let supportTS = true;
try {
require.resolve('typescript');
} catch (e) {
supportTS = false;
}
const TARGET = process.env.npm_lifecycle_event;
if (!TARGET) {
// eslint-disable-next-line no-console
console.log('Please run this script through the npm.');
}
const isDev = TARGET !== 'build';
const outputPath = path.resolve(__dirname, 'dist');
let copyPlugins = [];
if (existsSync(path.resolve(__dirname, 'public'))) {
copyPlugins = [
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, 'public'),
to: outputPath,
toType: 'dir',
},
]),
];
}
const cssLoader = modules => [
{
loader: 'css-loader',
options: {
importLoaders: 1,
...(isDev
? {}
: {
minimize: {
minifyFontValues: false,
},
sourceMap: true,
}),
...(!modules
? {}
: {
modules,
localIdentName: isDev
? '[name]__[local]___[hash:base64:5]'
: '[local]___[hash:base64:5]',
}),
},
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
sourceMap: isDev,
},
},
];
const lessLoader = {
loader: 'less-loader',
options: {
modifyVars: theme,
javascriptEnabled: true,
},
};
const sassLoader = {
loader: 'sass-loader',
};
// noinspection JSUnresolvedVariable
const cssRules = [
{
test: /\.css$/,
exclude: /node_modules/,
use: [
// 'style-loader',
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
...cssLoader(true),
],
},
{
test: /\.css$/,
include: /node_modules/,
use: [
// 'style-loader',
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
...cssLoader(false),
],
},
{
test: /\.less$/,
exclude: /node_modules/,
use: [
// 'style-loader',
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
...cssLoader(true),
lessLoader,
],
},
{
test: /\.less$/,
include: /node_modules/,
use: [
// 'style-loader',
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
...cssLoader(false),
lessLoader,
],
},
...(!hasSassLoader
? []
: [
{
test: /\.sass$/,
exclude: /node_modules/,
use: [
// 'style-loader',
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
...cssLoader(true),
sassLoader,
],
},
{
test: /\.sass$/,
include: /node_modules/,
use: [
// 'style-loader',
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
...cssLoader(false),
sassLoader,
],
},
]),
];
const config = {
context: path.resolve(__dirname),
entry: {
polyfill: './src/polyfill.js',
main: './src/index.js',
},
output: {
path: outputPath,
},
resolve: {
extensions: [
'.web.js',
'.web.jsx',
'.web.ts',
'.web.tsx',
'.js',
'.json',
'.jsx',
'.ts',
'.tsx',
],
plugins: [
...(!supportTS
? []
: [
new (require('awesome-typescript-loader').TsConfigPathsPlugin)(),
]),
],
},
module: {
rules: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'tslint-loader',
options: {
emitErrors: true,
},
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
enforce: 'pre',
loader: 'eslint-loader',
},
{
exclude: [
/\.(html|ejs)$/,
/\.json$/,
/\.(js|jsx|ts|tsx)$/,
/\.(css|less|scss|sass)$/,
/\.md$/,
],
loader: 'url-loader',
options: {
limit: 8192,
name: 'static/[name].[hash:8].[ext]',
},
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: [
'cache-loader',
'babel-loader',
],
},
{
test: /\.md$/,
loader: 'raw-loader',
},
...(!supportTS
? []
: [{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: [
'cache-loader',
'babel-loader',
{
loader: 'awesome-typescript-loader',
options: {
useTranspileModule: true,
},
},
],
}]),
{
test: /\.html$/,
loader: 'file-loader',
options: {
name: '[name].[ext]',
},
},
...cssRules,
],
},
plugins: [
new CleanWebpackPlugin(['dist']),
new CaseSensitivePathsPlugin(),
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new HtmlWebpackPlugin({
template: 'src/index.ejs',
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
}),
...copyPlugins,
],
};
module.exports = config;
module.exports = function (config) {
config.module.rules.push({
test: /\.ejs$/,
loader: 'ejs-loader',
});
return config;
};
const config = require('./config');
const webpack = require('webpack');
const merge = require('webpack-merge');
const applyMock = require('./tools/applyMock');
const common = require('./webpack.common.js');
const proxy = require('./proxy');
const register = require('./babel-register');
const mock = require('./mock');
register.unregister();
module.exports = merge(common, {
output: {
publicPath: config.dev.publicPath,
filename: '[name].js',
chunkFilename: '[name].async.js',
pathinfo: true,
},
mode: 'development',
devtool: 'inline-cheap-source-map',
devServer: {
disableHostCheck: true,
publicPath: config.dev.publicPath,
hot: true,
compress: true,
progress: true,
historyApiFallback: true,
proxy,
headers: {
'access-control-allow-origin': '*',
},
watchOptions: {
ignored: /node_modules/,
},
after(app) {
applyMock(app, mock);
},
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
],
});
const webpack = require('webpack');
const merge = require('webpack-merge');
const ManifestPlugin = require('webpack-manifest-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const safeParser = require('postcss-safe-parser');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const config = require('./config');
const common = require('./webpack.common.js');
module.exports = merge(common, {
output: {
publicPath: config.prod.publicPath,
filename: '[name].[chunkhash:8].js',
chunkFilename: '[name].[chunkhash:8].async.js',
},
mode: 'production',
plugins: [
new webpack.HashedModuleIdsPlugin(),
new ManifestPlugin({
publicPath: config.prod.publicPath,
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[id].[contenthash:8].css',
// allChunks: true,
}),
new BundleAnalyzerPlugin(),
],
optimization: {
concatenateModules: true,
minimizer: [
new UglifyJsPlugin({
parallel: true,
cache: true,
uglifyOptions: {
output: {
ascii_only: true,
},
},
}),
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: safeParser,
},
}),
],
},
});
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论