提交 cd5db3b3 authored 作者: vipcxj's avatar vipcxj

clean ignored file

上级 0b5f3875
{
"presets": ["./tools/babel-preset"]
}
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
{
"parser": "babel-eslint",
"extends": ["airbnb", "plugin:lodash-fp/recommended"],
"rules": {
"generator-star-spacing": [0],
"consistent-return": [0],
"react/forbid-prop-types": [0],
"react/jsx-filename-extension": [1, { "extensions": [".js"] }],
"global-require": [1],
"import/prefer-default-export": [0],
"react/jsx-no-bind": [0],
"react/prop-types": [0],
"react/prefer-stateless-function": [0],
"no-else-return": [0],
"no-restricted-syntax": [0],
"import/no-extraneous-dependencies": [0],
"no-use-before-define": [0],
"jsx-a11y/no-static-element-interactions": [0],
"no-nested-ternary": [0],
"arrow-body-style": [0],
"import/extensions": [0],
"no-bitwise": [0],
"no-cond-assign": [0],
"import/no-unresolved": [0],
"require-yield": [1],
"no-plusplus": "off",
"no-mixed-operators": "off",
"max-len": [0, 120],
"object-curly-newline": "off",
"padded-blocks": "off",
"function-paren-newline": "off",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/click-events-have-key-events": "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
# production
/dist
/.idea
# misc
.DS_Store
npm-debug.log*
/storybook-static
jsCodeStructure.html
/webpack/**
!/webpack/.gitkeep
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
<component name="ProjectDictionaryState">
<dictionary name="yaohx_169" />
</component>
\ No newline at end of file
<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
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/app-manage-console.iml" filepath="$PROJECT_DIR$/.idea/app-manage-console.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RTProjectComponent">
<option name="nodeInterpreter" value="" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="f3473cde-6bad-431d-8195-0b704f7cfba4" name="Default" comment="" />
<ignored path="$PROJECT_DIR$/.tmp/" />
<ignored path="$PROJECT_DIR$/temp/" />
<ignored path="$PROJECT_DIR$/tmp/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="TRACKING_ENABLED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file leaf-file-name="list.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/list.js">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="26" selection-start-column="26" selection-end-column="26" />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="index.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/models/main/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1155">
<caret line="77" selection-start-line="77" selection-end-line="77" />
<folding>
<element signature="e#0#41#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="index.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/models/main/modules/appManagement/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="630">
<caret line="42" column="41" selection-start-line="42" selection-start-column="41" selection-end-line="42" selection-end-column="41" />
<folding>
<element signature="e#0#50#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="package.json" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="237">
<caret line="41" column="27" selection-start-line="41" selection-start-column="27" selection-end-line="41" selection-end-column="27" />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="config.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/utils/config.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="274">
<caret line="65" column="34" lean-forward="true" selection-start-line="65" selection-start-column="34" selection-end-line="65" selection-end-column="34" />
</state>
</provider>
</entry>
</file>
<file leaf-file-name="index.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/services/login/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-618">
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="router.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/router.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1603">
<folding>
<element signature="n#!!doc" expanded="true" />
<element signature="e#2916#5517#0" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="login.js" pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/models/login.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1026">
<caret line="41" selection-start-line="41" selection-end-line="41" />
<folding>
<element signature="e#0#41#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file leaf-file-name="app.js" pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/src/services/app.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" selection-start-line="2" selection-end-line="4" selection-end-column="39" />
<folding>
<element signature="n#!!doc" expanded="true" />
<element signature="e#20#59#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>normRoutes</find>
<find>routes</find>
<find>values</find>
<find>isNumber</find>
</findStrings>
</component>
<component name="Git.Settings">
<option name="ROOT_SYNC" value="DONT_SYNC" />
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/.eslintrc" />
<option value="$PROJECT_DIR$/src/routes/main/header.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/index.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/add.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/addDeploy.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/deploy.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/edit.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/editDeploy.js" />
<option value="$PROJECT_DIR$/src/routes/main/modules/appManagement/list.js" />
<option value="$PROJECT_DIR$/src/models/main/modules/appManagement/index.js" />
<option value="$PROJECT_DIR$/src/utils/config.js" />
<option value="$PROJECT_DIR$/src/models/login.js" />
<option value="$PROJECT_DIR$/src/services/app.js" />
</list>
</option>
</component>
<component name="JsBowerSettings">
<node-interpreter value="project" />
<exe-path />
<config-path />
</component>
<component name="JsBuildToolGruntFileManager" detection-done="true" sorting="DEFINITION_ORDER" />
<component name="JsBuildToolPackageJson" detection-done="true" sorting="DEFINITION_ORDER">
<package-json value="$PROJECT_DIR$/package.json" />
</component>
<component name="JsFlowSettings">
<service-enabled>true</service-enabled>
<exe-path />
<annotation-enable>false</annotation-enable>
<other-services-enabled>true</other-services-enabled>
</component>
<component name="JsGulpfileManager">
<detection-done>true</detection-done>
<sorting>DEFINITION_ORDER</sorting>
</component>
<component name="NodeModulesDirectoryManager">
<handled-path value="$PROJECT_DIR$/node_modules" />
</component>
<component name="NodePackageJsonFileManager">
<packageJsonPaths>
<path value="$PROJECT_DIR$/package.json" />
</packageJsonPaths>
</component>
<component name="ProjectFrameBounds">
<option name="x" value="-16" />
<option name="y" value="26" />
<option name="width" value="1440" />
<option name="height" value="806" />
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state>
<expanded-state>
<State>
<id />
</State>
<State>
<id>JavaScript</id>
</State>
</expanded-state>
<selected-state>
<State>
<id>DOM issuesJavaScript</id>
</State>
</selected-state>
</profile-state>
</entry>
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="app-manage-console" type="b2602c69:ProjectViewProjectNode" />
<item name="app-manage-console" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="app-manage-console" type="b2602c69:ProjectViewProjectNode" />
<item name="app-manage-console" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="app-manage-console" type="b2602c69:ProjectViewProjectNode" />
<item name="app-manage-console" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="models" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="app-manage-console" type="b2602c69:ProjectViewProjectNode" />
<item name="app-manage-console" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="models" type="462c0819:PsiDirectoryNode" />
<item name="main" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="app-manage-console" type="b2602c69:ProjectViewProjectNode" />
<item name="app-manage-console" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="services" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="app-manage-console" type="b2602c69:ProjectViewProjectNode" />
<item name="app-manage-console" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="utils" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
<pane id="Scope" />
</panes>
</component>
<component name="PropertiesComponent">
<property name="HbShouldOpenHtmlAsHb" value="" />
<property name="JavaScriptWeakerCompletionTypeGuess" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.standard" value="true" />
<property name="node.js.path.for.package.eslint" value="project" />
<property name="node.js.path.for.package.standard" value="project" />
<property name="node.js.path.for.package.tslint" value="project" />
<property name="node.js.selected.package.eslint" value="$PROJECT_DIR$/node_modules/eslint" />
<property name="node.js.selected.package.standard" value="$PROJECT_DIR$/node_modules/eslint" />
<property name="node.js.selected.package.tslint" value="" />
<property name="nodejs_interpreter_path.stuck_in_default_project" value="/usr/local/bin/node" />
<property name="nodejs_npm_path_reset_for_default_project" value="true" />
<property name="nodejs_package_manager_path" value="npm" />
<property name="settings.editor.selected.configurable" value="settings.javascript.linters.eslint" />
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="npm.start">
<configuration name="lint" type="js.build_tools.npm" factoryName="npm" temporary="true" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="lint" />
</scripts>
<node-interpreter value="project" />
<envs />
</configuration>
<configuration name="start" type="js.build_tools.npm" factoryName="npm" temporary="true" nameIsGenerated="true">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="start" />
</scripts>
<node-interpreter value="project" />
<envs />
</configuration>
<list>
<item itemvalue="npm.start" />
<item itemvalue="npm.lint" />
</list>
<recent_temporary>
<list>
<item itemvalue="npm.start" />
<item itemvalue="npm.lint" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="f3473cde-6bad-431d-8195-0b704f7cfba4" name="Default" comment="" />
<created>1544071389238</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1544071389238</updated>
<workItem from="1544071391093" duration="3544000" />
<workItem from="1544074982030" duration="1269000" />
</task>
<task id="LOCAL-00001" summary="Merge remote-tracking branch 'scaffold/master'&#10;&#10;# Conflicts:&#10;#&#9;.roadhogrc.js&#10;#&#9;package.json&#10;#&#9;src/models/domain.js&#10;#&#9;src/models/main/index.js&#10;#&#9;src/router.js&#10;#&#9;src/routes/domain/index.js&#10;#&#9;src/routes/main/header.js&#10;&#10;更新到最新的脚手架">
<created>1544076221219</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1544076221219</updated>
</task>
<option name="localTasksCounter" value="2" />
<servers />
</component>
<component name="TimeTrackingManager">
<option name="totallyTimeSpent" value="4813000" />
</component>
<component name="ToolWindowManager">
<frame x="-16" y="26" width="1440" height="806" extended-state="0" />
<layout>
<window_info anchor="right" id="GfmBrowser" order="3" />
<window_info anchor="bottom" id="TODO" order="6" />
<window_info anchor="bottom" id="Messages" weight="0.32900432" />
<window_info anchor="bottom" id="Event Log" order="7" sideWeight="0.5007153" side_tool="true" weight="0.32756132" />
<window_info anchor="bottom" id="Version Control" order="7" weight="0.2496474" />
<window_info id="npm" order="2" sideWeight="0.50429183" side_tool="true" visible="true" weight="0.24964234" />
<window_info anchor="bottom" id="Run" order="2" weight="0.32756132" />
<window_info active="true" anchor="bottom" id="Terminal" order="7" sideWeight="0.49928468" visible="true" weight="0.32756132" />
<window_info anchor="bottom" id="RNConsole" order="7" />
<window_info content_ui="combo" id="Project" order="0" sideWeight="0.49570817" visible="true" weight="0.24964234" />
<window_info anchor="bottom" id="Docker" order="7" show_stripe_button="false" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="right" id="Commander" order="0" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Find" order="1" />
</layout>
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="1" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State>
<option name="RECENTLY_FILTERED_USER_GROUPS">
<collection />
</option>
<option name="RECENTLY_FILTERED_BRANCH_GROUPS">
<collection>
<UserGroup>
<option name="users">
<list>
<option value="scaffold/master" />
</list>
</option>
</UserGroup>
</collection>
</option>
<option name="FILTERS">
<map>
<entry key="branch">
<value>
<list>
<option value="scaffold/master" />
</list>
</value>
</entry>
</map>
</option>
<option name="COLUMN_ORDER">
<list>
<option value="0" />
<option value="1" />
<option value="2" />
<option value="3" />
</list>
</option>
</State>
</value>
</entry>
</map>
</option>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Merge remote-tracking branch 'scaffold/master'&#10;&#10;# Conflicts:&#10;#&#9;.roadhogrc.js&#10;#&#9;package.json&#10;#&#9;src/models/domain.js&#10;#&#9;src/models/main/index.js&#10;#&#9;src/router.js&#10;#&#9;src/routes/domain/index.js&#10;#&#9;src/routes/main/header.js&#10;&#10;更新到最新的脚手架" />
<option name="LAST_COMMIT_MESSAGE" value="Merge remote-tracking branch 'scaffold/master'&#10;&#10;# Conflicts:&#10;#&#9;.roadhogrc.js&#10;#&#9;package.json&#10;#&#9;src/models/domain.js&#10;#&#9;src/models/main/index.js&#10;#&#9;src/router.js&#10;#&#9;src/routes/domain/index.js&#10;#&#9;src/routes/main/header.js&#10;&#10;更新到最新的脚手架" />
</component>
<component name="editorHistoryManager">
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/deploy.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="26" selection-start-line="4" selection-start-column="26" selection-end-line="4" selection-end-column="26" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/edit.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" column="43" selection-start-line="1" selection-start-column="43" selection-end-line="1" selection-end-column="43" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="120">
<caret line="15" column="16" selection-start-line="15" selection-start-column="16" selection-end-line="15" selection-end-column="16" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/list.js">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="26" selection-start-column="26" selection-end-column="26" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/models/main/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1155">
<caret line="77" selection-start-line="77" selection-end-line="77" />
<folding>
<element signature="e#0#41#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/models/main/modules/appManagement/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="630">
<caret line="42" column="41" selection-start-line="42" selection-start-column="41" selection-end-line="42" selection-end-column="41" />
<folding>
<element signature="e#0#50#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/add.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" column="43" selection-start-line="1" selection-start-column="43" selection-end-line="1" selection-end-column="43" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/editDeploy.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="51" selection-start-line="4" selection-start-column="51" selection-end-line="4" selection-end-column="51" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/addDeploy.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" column="80" selection-start-line="5" selection-start-column="80" selection-end-line="5" selection-end-column="80" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="735">
<caret line="49" column="26" selection-start-line="49" selection-start-column="26" selection-end-line="49" selection-end-column="26" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/.roadhogrc.js" />
<entry file="file://$PROJECT_DIR$/.eslintrc">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="40">
<caret line="32" column="51" selection-start-line="32" selection-start-column="51" selection-end-line="32" selection-end-column="51" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/header.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="230">
<caret line="100" column="17" lean-forward="true" selection-start-line="100" selection-start-column="17" selection-end-line="100" selection-end-column="17" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/add.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" column="43" selection-start-line="1" selection-start-column="43" selection-end-line="1" selection-end-column="43" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/addDeploy.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="75">
<caret line="5" column="80" selection-start-line="5" selection-start-column="80" selection-end-line="5" selection-end-column="80" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/deploy.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="26" selection-start-line="4" selection-start-column="26" selection-end-line="4" selection-end-column="26" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/edit.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" column="43" selection-start-line="1" selection-start-column="43" selection-end-line="1" selection-end-column="43" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/editDeploy.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="51" selection-start-line="4" selection-start-column="51" selection-end-line="4" selection-end-column="51" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="120">
<caret line="15" column="16" selection-start-line="15" selection-start-column="16" selection-end-line="15" selection-end-column="16" />
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/routes/main/modules/appManagement/list.js">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="26" selection-start-column="26" selection-end-column="26" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/models/main/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="1155">
<caret line="77" selection-start-line="77" selection-end-line="77" />
<folding>
<element signature="e#0#41#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/models/main/modules/appManagement/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="630">
<caret line="42" column="41" selection-start-line="42" selection-start-column="41" selection-end-line="42" selection-end-column="41" />
<folding>
<element signature="e#0#50#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/package.json">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="237">
<caret line="41" column="27" selection-start-line="41" selection-start-column="27" selection-end-line="41" selection-end-column="27" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/utils/config.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="274">
<caret line="65" column="34" lean-forward="true" selection-start-line="65" selection-start-column="34" selection-end-line="65" selection-end-column="34" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/services/login/index.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-618">
<folding>
<element signature="n#!!doc" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/router.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1603">
<folding>
<element signature="n#!!doc" expanded="true" />
<element signature="e#2916#5517#0" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/models/login.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="-1026">
<caret line="41" selection-start-line="41" selection-end-line="41" />
<folding>
<element signature="e#0#41#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/utils/auth.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="60">
<caret line="4" column="7" lean-forward="true" selection-start-line="4" selection-start-column="7" selection-end-line="4" selection-end-column="7" />
<folding>
<element signature="n#!!doc" expanded="true" />
<element signature="e#84#126#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/utils/helper.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="15">
<caret line="1" column="7" lean-forward="true" selection-start-line="1" selection-start-column="7" selection-end-line="1" selection-end-column="7" />
<folding>
<element signature="n#!!doc" expanded="true" />
<element signature="e#39#67#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/webpack.dev.js">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret column="15" selection-start-column="15" selection-end-column="15" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/services/app.js">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="30">
<caret line="2" selection-start-line="2" selection-end-line="4" selection-end-column="39" />
<folding>
<element signature="n#!!doc" expanded="true" />
<element signature="e#20#59#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</component>
</project>
\ No newline at end of file
{
"presets": ["../tools/babel-preset"]
}
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
import '@babel/polyfill';
import { configure } from '@storybook/react';
import('../stories').then((stories) => {
configure(() => stories, module);
});
const createProxy = require('http-proxy-middleware');
const forEach = require('lodash/forEach');
require('../babel-register');
const proxy = require('../proxy');
const mock = require('../mock');
const applyMock = require('../tools/applyMock');
module.exports = (app) => {
forEach(proxy, (value, key) => {
app.use(key, createProxy(key, value));
});
applyMock(app, mock);
};
let config;
if (process.env.NODE_ENV === 'production') {
config = require('../webpack.prod');
} else {
config = require('../webpack.dev');
}
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');
babel.default({
presets: [
['./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.default({
extensions: [],
});
},
};
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';
const modules = genModules();
const tasks = getTasks();
const domains = [
{
id: 1,
name: '虚拟基地01',
},
{
id: 2,
name: '虚拟基地02',
},
{
id: 3,
name: '虚拟基地03',
},
{
id: 4,
name: '虚拟基地04',
},
{
id: 5,
name: '虚拟基地05',
},
];
const getDomain = (id) => {
const domain = domains.filter(domain => domain.id === id);
return domain ? domain[0] : null;
};
const dealWithData = (req) => {
const { sort, order } = req.query;
const filters = [];
for (const queryKey in req.query) {
if (queryKey.indexOf('f-') === 0) {
filters.push(toFilters(queryKey, req.query[queryKey]));
}
}
let data = tasks.filter((value) => {
return filters.map(filter => filter(value)).reduce((ret, cur) => ret && cur, true);
});
if (sort) {
data = data.sort((a, b) => {
const va = a[sort];
const vb = b[sort];
if (order === 'desc') {
if (moment.isMoment(va) || moment.isDate(va) || moment.isMoment(vb) || moment.isDate(vb)) {
if (moment(va).isAfter(moment(vb))) {
return 1;
} else if (moment(va).isSame(moment(vb))) {
return 0;
} else {
return -1;
}
} else if (va > vb) {
return 1;
} else if (va === vb) {
return 0;
} else {
return -1;
}
} else if (moment.isMoment(va) || moment.isDate(va) || moment.isMoment(vb) || moment.isDate(vb)) {
if (moment(va).isBefore(moment(vb))) {
return 1;
} else if (moment(va).isSame(moment(vb))) {
return 0;
} else {
return -1;
}
} else if (va < vb) {
return 1;
} else if (va === vb) {
return 0;
} else {
return -1;
}
});
}
return data;
};
let currentDomainId = null;
const wrapResponse = (response) => {
return {
errorCode: 0,
data: response,
};
};
module.exports = {
'/api/user/logout': (req, res) => {
res.status(204).end();
},
'/api/domain/all': wrapResponse(domains),
'/api/domain/switch': (req, res) => {
const { domainId } = req.query;
const intDomainId = parseInt(domainId, 10);
const domainIds = domains.map(domain => domain.id);
if (domainIds.indexOf(intDomainId) !== -1) {
if (currentDomainId) {
res.send(wrapResponse(getDomain(currentDomainId)));
} else {
res.status(204).end();
}
currentDomainId = intDomainId;
} else {
res.status(500).send({
errorCode: 0x00010010,
message: '无效的项目ID。',
});
}
},
'/api/domain/current': (req, res) => {
res.send(wrapResponse(getDomain(currentDomainId)));
},
'/api/module/all/info': (req, res) => {
console.log('/api/module/all/info');
const { all } = modules;
const publics = modules.public;
const findModule = id => all.filter(m => m.id === id).pop();
const fetchParent = (module) => {
if (module.parent) {
const parent = findModule(module.parent);
return parent ? [parent, ...fetchParent(parent)] : [];
} else {
return [];
}
};
const parents = new Set();
publics.forEach((m) => {
const p0 = fetchParent(m);
p0.forEach((p) => {
parents.add(p);
});
});
res.send(wrapResponse([
...publics,
...parents,
]));
},
'/api/bpm/task/all/count': (req, res) => {
res.send(wrapResponse(dealWithData(req).length));
},
'/api/bpm/task/all/info': (req, res) => {
const pst = Number.parseInt(req.query.pst, 10);
const psz = Number.parseInt(req.query.psz, 10);
res.send(wrapResponse(dealWithData(req).slice(pst, pst + psz)));
},
};
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"private": true,
"scripts": {
"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": "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": {
"@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",
"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.22.2",
"prop-types": "^15.6.2",
"qrcode": "^1.0.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.19"
},
"devDependencies": {
"@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",
"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,
};
/**
* Created by yaohx_169 on 2017/7/10.
*/
/**
* Created by yaohx_169 on 2017/7/10.
*/
/**
* Created by yaohx_169 on 2017/7/10.
*/
import { makePath } from '../../utils/helper';
const route = (routes) => {
const Wrapper = ({ children }) => {
return children || null;
};
if (routes.indexRoute) {
Wrapper.route = routes;
} else {
Wrapper.route = {
indexRoute: {
onEnter(nextState, replace) {
if (routes && routes.childRoutes && routes.childRoutes.length > 0) {
const index = routes.childRoutes[0];
replace(makePath(nextState.match.url, index.path));
}
},
},
...routes,
};
}
return Wrapper;
};
export default route;
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/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) => {
const { dispatchVar, namespaceVar } = (options || {});
class StatefulComponent extends Component {
constructor(props, context) {
super(props, context);
const { name, model } = modelCreator();
this.name = name;
this.model = model;
const mapState = (state) => {
const pps = mapStateToProps ? mapStateToProps(state) : {};
pps[name] = state[model.namespace];
pps[namespaceVar || 'namespace'] = model.namespace;
if (state.loading) {
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 pps;
};
const mapDispatch = (dispatch) => {
const extras = mapDispatchToProps ? mapDispatchToProps(dispatch) : {};
return {
dispatch,
...extras,
[dispatchVar || 'dispatchLocal'](action) {
const { type, payload } = action;
return dispatch({ type: `${model.namespace}/${type}`, payload });
},
};
};
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 (
<this.Output {...rest}>
{ children }
</this.Output>
);
}
}
return StatefulComponent;
};
export default connect;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { filterValidParams } from '../../utils/helper';
class TwoBind extends Component {
constructor(props, context) {
super(props, context);
this.state = {
value: '',
};
}
componentWillReceiveProps(nextProps) {
this.setState({
value: nextProps[nextProps.valueKey],
});
}
render() {
const { valueKey, cbKey, cbMap, wrapped, children, ...rest } = this.props.children.props;
const Wrapped = wrapped;
const callback = (cbValue) => {
this.setState({
value: cbMap(cbValue),
});
};
const props = {
[valueKey]: this.state.value,
[cbKey]: callback,
};
return (
<Wrapped {...rest} {...props}>
{ children }
</Wrapped>
);
}
}
TwoBind.propTypes = {
valueKey: PropTypes.string,
cbKey: PropTypes.string,
cbMap: PropTypes.func,
wrapped: PropTypes.func.isRequired,
};
const fucIdentity = value => value;
TwoBind.defaultProps = {
valueKey: 'value',
cbKey: 'onChange',
cbMap: fucIdentity,
};
const makeTwoBind = ({ valueKey, cbKey, cbMap } = {}) => (Comp) => {
const params = filterValidParams({ valueKey, cbKey, cbMap });
return <TwoBind {...params} wrapped={Comp} />;
};
export default makeTwoBind;
import React from 'react';
import { addEvent, removeEvent } from '../../utils/dom';
class RootPanel extends React.Component {
constructor(props, context) {
super(props, context);
this.attachNode = this::this.attachNode;
this.updateHeight = this::this.updateHeight;
this.state = {
height: 0,
};
}
componentDidMount() {
addEvent(window, 'resize', this.updateHeight); // eslint-disable-line no-undef
this.updateHeight();
}
componentWillUnmount() {
removeEvent(window, 'resize', this.updateHeight); // eslint-disable-line no-undef
}
updateHeight() {
const parentNode = this.node.parentNode;
this.setState({
height: parentNode.offsetHeight,
});
}
attachNode(node) {
this.node = node;
}
render() {
const { children, style, ...rest } = this.props;
return (
<div style={{ ...style, height: this.state.height }} {...rest} ref={this.attachNode}>{ children }</div>
);
}
}
export default RootPanel;
import React from 'react';
import config from '../../../utils/config';
import styles from './SiderLogo.less';
const SiderLogo = () => {
return (
<div className={styles.logo}>
<img alt="logo" src={config.logo} />
<span>{config.name}</span>
</div>
);
};
export default SiderLogo;
.logo {
text-align: center;
height: 30px;
line-height: 30px;
cursor: pointer;
margin-top: 24px;
margin-bottom: 24px;
overflow: hidden;
img {
width: 30px;
margin-right: 8px;
}
span {
vertical-align: top;
font-size: 16px;
color: #eeeeee;
text-transform: uppercase;
display: inline-block;
}
}
:global .ant-layout-sider-collapsed {
:local .logo {
height: 24px;
line-height: 24px;
img {
width: 24px;
margin-left: 8px;
}
span {
display: none;
}
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { Menu, Icon } from 'antd';
import styles from './SiderMenu.less';
const SubMenu = Menu.SubMenu;
const MenuItem = Menu.Item;
class SiderMenu extends React.Component {
constructor(props, context) {
super(props, context);
this.createMenus = this::this.createMenus;
}
createMenus() {
const menus = this.props.menus;
const mode = this.props.mode;
const toTitle = (menu, root = true) => {
const text = (mode !== 'inline' && root) ? '' : menu.text;
if (menu.icon) {
return (
<span>
<Icon type={menu.icon} />
{ text }
</span>
);
} else {
return text;
}
};
const toMenuGroup = (menu, toMenuItemFunc, root = false) => {
if (!menu.children || menu.children.length === 0) {
return toMenuItemFunc(menu, toMenuGroup, root);
}
return (
<SubMenu title={toTitle(menu, root)} key={menu.name}>
{ menu.children.map(child => toMenuItemFunc(child, toMenuGroup)) }
</SubMenu>
);
};
const toMenuItem = (menu, toMenuGroupFunc, root = false) => {
if (menu.children && menu.children.length !== 0) {
return toMenuGroupFunc(menu, toMenuItem, root);
}
return (
<MenuItem key={menu.name}>
{ toTitle(menu, root) }
</MenuItem>
);
};
return menus.map(menu => toMenuGroup(menu, toMenuItem, true));
}
render() {
const menuProps = {
className: styles.menu,
theme: this.props.theme,
mode: this.props.mode,
onClick: this.props.onClick,
};
return (
<Menu {...menuProps}>
{ this.createMenus() }
</Menu>
);
}
}
SiderMenu.propTypes = {
theme: PropTypes.string,
mode: PropTypes.string,
menus: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string,
icon: PropTypes.string,
text: PropTypes.string,
children: PropTypes.array,
})),
};
SiderMenu.defaultProps = {
theme: 'dark',
mode: 'inline',
menus: [],
};
export default SiderMenu;
:global .ant-layout-sider-collapsed {
:local .menu {
:global .anticon {
font-size: 16px;
margin-left: 8px;
}
:global .ant-menu-submenu-vertical > .ant-menu-submenu-title:after {
display: none;
}
}
}
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 '../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, current = 1, start, end } = this.props;
dispatchLocal({ type: 'doInit', payload: { coordinate, params, current, start, end } });
}
componentWillReceiveProps(nextProps) {
const { coordinate, params, current, start, end } = nextProps;
const { dispatchLocal } = this.props;
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, 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,
pagination: {
current,
total: num,
pageSize,
},
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 });
}
const { field, order } = sorter;
if (field) {
dispatchLocal({ type: 'doSort', payload: { field, order } });
}
},
onFilter: (theFilters) => {
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}>
<TableEx {...tableProps} />
</div>
</div>
);
}
}
DsTable.propTypes = {
coordinate: PropTypes.oneOfType(
[
PropTypes.string,
PropTypes.shape({
containerType: PropTypes.string,
containerName: PropTypes.string,
datasourceName: PropTypes.string,
}),
],
).isRequired,
onPageChange: PropTypes.func,
current: PropTypes.number,
};
DsTable.defaultProps = {
onPageChange: () => null,
};
export default connect(modelCreator, {
mapStateToProps: ({ loading }) => ({ loading }),
})(DsTable);
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 12px;
}
.container {
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
import uuid from 'uuid/v4';
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 getSource = (property) => {
if (property.source) {
return (property.source.items || []).map((item) => {
try {
return JSON.parse(item);
} catch (err) {
return item;
}
});
} else {
return [];
}
};
const makeProps = (meta) => {
// noinspection JSUnresolvedVariable
if (!meta || !meta.metas) {
return {};
}
const props = normMetas(meta.metas);
if (!props.rowKey) {
props.rowKey = getKeyName(meta);
}
return props;
};
const getColumnIdx = (columns, name) => {
return (columns || []).findIndex(column => name === column.dataIndex);
};
const makeColumns = (meta) => {
return (meta.properties || [])
.filter(property => !property.skip)
.map((property) => {
// noinspection JSUnresolvedVariable
const props = normMetas(property.metas);
if (!props.title && props.label) {
props.title = props.label;
}
if (props.order === undefined) {
props.order = 0;
}
if ((props.fixed === true || props.fixed === 'left' || props.fixed === 'right') && props.width === undefined) {
props.width = 150;
}
return pickBy({
...props,
dataIndex: property.name,
key: property.name,
sorter: property.sort,
filterType: property.filterType,
filterEnums: getSource(property),
}, negate(isUndefined));
})
.filter((c) => {
for (const key of Object.keys(c)) {
if (key.startsWith('meta:')) {
return false;
}
}
return c.visible !== false;
})
.sort((c1, c2) => {
const c1Left = c1.fixed === true || c1.fixed === 'left';
const c1Right = c1.fixed === 'right';
const c2Left = c2.fixed === true || c2.fixed === 'left';
const c2Right = c2.fixed === 'right';
if (c1Left && !c2Left) {
return -1;
}
if (!c1Left && c2Left) {
return 1;
}
if (c1Right && !c2Right) {
return 1;
}
if (!c1Right && c2Right) {
return -1;
}
return c1.order - c2.order;
});
};
const getSort = (columns) => {
const column = (columns || []).find(c => c.sortOrder);
return column ? {
field: column.key,
order: column.sortOrder,
} : undefined;
};
const modelCreator = () => {
const name = 'model';
const namespace = uniqueId(prefix);
const loadMeta = function* loadMeta({ select, call, put }) {
const { coordinate, params } = yield select(state => state[namespace]);
const api = datasourceApi(coordinate);
const meta = yield call(api.queryMeta, { params });
yield put({ type: 'applyMeta', payload: meta });
};
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;
const pst = (current - 1) * psz;
const sort = getSort(columns);
const sortBys = sort ? [sort.field] : [];
const sortTypes = sort ? [sort.order] : [];
const options = { pst, psz, params, filters, sortBys, sortTypes };
const num = yield call(api.count, options);
const dsb = yield call(api.query, options);
const list = parseQueryResult(dsb, meta);
yield put({ type: 'applyData', payload: { num, list } });
} finally {
if (end) {
yield put({ type: end });
}
}
};
return {
name,
model: {
namespace,
state: {
coordinate: null,
params: {},
meta: {},
parsedMeta: {
global: {
columnData: {},
rowData: {},
},
properties: {},
},
columns: [],
props: {},
num: 0,
list: [],
filters: [],
current: 1,
pageSize: 10,
},
reducers: {
applyTarget(state, { payload: { coordinate, params } }) {
return {
...state,
coordinate,
params,
};
},
applyFilters(state, { payload: filters }) {
return {
...state,
filters,
};
},
changeSorts(state, { payload: { field, order } }) {
const idx = getColumnIdx(state.columns, field);
if (idx !== -1) {
const columns = [...state.columns];
for (let i = 0; i < columns.length; ++i) {
if (i !== idx) {
columns[i].sortOrder = false;
} else {
columns[i].sortOrder = order;
}
}
return {
...state,
columns,
};
} else {
return state;
}
},
applyCurrentPage(state, { payload: current }) {
return {
...state,
current,
};
},
applyPageSize(state, { payload: pageSize }) {
return {
...state,
pageSize,
};
},
applyMeta(state, { payload: meta }) {
return {
...state,
meta,
parsedMeta: parseMetas(meta),
props: makeProps(meta),
columns: makeColumns(meta),
};
},
applyData(state, { payload: { num, list } }) {
return {
...state,
num,
list,
};
},
queryMetaSuccess(state, { payload: { props, columns } }) {
return {
...state,
props,
columns,
};
},
queryCountSuccess(state, { payload: num }) {
return {
...state,
num,
};
},
queryTasksSuccess(state, { payload: list }) {
return {
...state,
list,
};
},
},
effects: {
*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: current });
yield call(loadMeta, { put, call, select });
yield call(loadData, { put, call, select, start, end });
},
*doFilter({ payload: filters }, { put, call, select }) {
yield put({ type: 'applyFilters', payload: filters });
yield put({ type: 'applyCurrentPage', payload: 1 });
yield call(loadData, { put, call, select });
},
*doSort({ payload: { field, order } }, { put, call, select }) {
yield put({ type: 'changeSorts', payload: { field, order } });
yield put({ type: 'applyCurrentPage', payload: 1 });
yield call(loadData, { put, call, select });
},
*changeCurrentPage({ payload: current }, { put, call, select }) {
yield put({ type: 'applyCurrentPage', payload: current });
yield call(loadData, { put, call, select });
},
*changePageSize({ payload: pageSize }, { put, call, select }) {
yield put({ type: 'applyCurrentPage', payload: 1 });
yield put({ type: 'applyPageSize', payload: pageSize });
yield call(loadData, { put, call, select });
},
// *fetchData({ payload: { coordinate, pst, psz, params, filters} }, { put, call }) {
// const options = { pst, psz, params, filters };
// const api = datasourceApi(coordinate);
// const meta = yield call(api.meta);
// const props = makeProps(meta);
// yield put({ type: 'queryMetaSuccess', payload: { props, columns: makeColumns(meta) } });
// const num = yield call(api.count, options);
// yield put({ type: 'queryCountSuccess', payload: num });
// const dsb = yield call(api.query, options);
// yield put({ type: 'queryTasksSuccess', payload: getArrayData(dsb, meta) });
// },
},
subscriptions: {},
},
};
};
export default modelCreator;
import React from 'react';
class DSTable extends React.PureComponent {
}
export default DSTable;
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 {
static initColumns(props) {
return (props.columns || []).map((column, index) => {
const filter = props.filters ? props.filters[index] : null;
return {
key: column.dataIndex,
filtered: !!filter,
filterDropdownVisible: false,
filter,
};
});
}
constructor(props, context) {
super(props, context);
const columns = TableEx.initColumns(props);
this.state = {
columns,
};
}
componentWillReceiveProps(nextProps) {
if (nextProps.columns !== this.props.columns) {
const columns = TableEx.initColumns(nextProps);
this.setState({
columns,
});
}
}
makeColumns() {
if (!this.props.columns) {
return [];
} else {
const { parsedMeta, renderCell } = this.props;
return this.props.columns.map((column, index) => {
const ret = {
...column,
};
const state = this.state.columns[index];
if (ret.filterType) {
const onSearch = (filter) => {
this.setState((preState) => {
const newColumns = [...preState.columns];
newColumns[index].filter = filter;
newColumns[index].filtered = !!filter;
newColumns[index].filterDropdownVisible = false;
return { columns: newColumns };
}, () => {
this.props.onFilter(this.state.columns.map(c => ({
key: c.key,
filter: c.filter,
})));
});
};
ret.filterDropdown = (
<Search type={ret.filterType} onSearchSubmit={onSearch} filterEnums={ret.filterEnums} />
);
ret.filterDropdownVisible = state.filterDropdownVisible;
ret.onFilterDropdownVisibleChange = (visible) => {
this.setState((preState) => {
const newColumns = [...preState.columns];
newColumns[index].filterDropdownVisible = visible;
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()} />
);
}
}
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 castArray from 'lodash/castArray';
import noop from 'lodash/noop';
import { Table, Button, Form } from 'antd';
class TableInput extends Component {
constructor(props, context) {
super(props, context);
this.onAdd = this::this.onAdd;
this.onOk = this::this.onOk;
this.state = {
editing: false,
operation: 'none',
};
}
onAdd() {
this.setState({
editing: true,
operation: 'add',
});
}
onOk() {
const { value, items, form, onChange } = this.props;
const row = {};
castArray(items).forEach((item) => {
row[item.name] = form.getFieldValue(item.name);
});
if (this.state.operation === 'add') {
(onChange || noop)([...(value || []), row]);
}
this.setState({
editing: false,
operation: 'none',
});
}
makeColumns() {
const { items } = this.props;
return castArray(items)
.map((item) => {
return {
...item,
title: item.label,
key: item.name,
dataIndex: item.name,
};
});
}
makeDataSource() {
const { value } = this.props;
return castArray(value || [])
.map((row, idx) => ({
...row,
__key__: idx,
}));
}
render() {
const { items, form, children } = this.props;
if (this.state.editing) {
const fields = castArray(children)
.map(
(child, i) => {
const item = items[i];
return (
<Form.Item {...item}>
{
form.getFieldDecorator(item.name, item)(
child,
)
}
</Form.Item>
);
},
this,
);
return (
<div>
{[
...fields,
<Button onClick={this.onOk}>确定</Button>,
]}
</div>
);
} else {
return (
<div>
<Button onClick={this.onAdd}>新增</Button>
<Table rowKey="__key__" dataSource={this.makeDataSource()} columns={this.makeColumns()} />
</div>
);
}
}
}
export default Form.create()(TableInput);
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { DatePicker, Button } from 'antd';
import moment from 'moment';
import config from '../../../utils/config';
import styles from './date.less';
const RangePicker = DatePicker.RangePicker;
class DateSearch extends Component {
constructor(props, context) {
super(props, context);
this.onClick = this::this.onClick;
this.onChange = this::this.onChange;
this.onOk = this::this.onOk;
this.bindContainer = this::this.bindContainer;
this.state = {
value: [moment(), moment()],
};
}
onClick() {
const [start, end] = this.state.value;
let sValue = '';
let eValue = '';
if (start && start.isValid()) {
sValue = start.valueOf().toString();
}
if (end && end.isValid) {
eValue = end.valueOf().toString();
}
const filter = (sValue || eValue) ? `~[${sValue},${eValue})` : null;
this.props.onSearchSubmit(filter);
}
onChange(value) {
this.setState({
value,
});
}
onOk(value) {
this.setState({
value,
});
}
bindContainer(node) {
this.container = node;
}
render() {
return (
<div ref={this.bindContainer} className={styles.container}>
<RangePicker
showTime
value={this.state.value}
format={config.defaultDateTimeFormat}
placeholder={['开始时间', '结束时间']}
onChange={this.onChange}
onOk={this.onOk}
getCalendarContainer={() => this.container}
/>
<Button className={styles.popupOk} onClick={this.onClick}>ok</Button>
</div>
);
}
}
DateSearch.propTypes = {
onSearchSubmit: PropTypes.func,
};
const funcVoid = () => {};
DateSearch.defaultProps = {
onSearchSubmit: funcVoid,
};
export default DateSearch;
@import "index";
.container:extend(.popup) {}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Checkbox, Button } from 'antd';
import isString from 'lodash/isString';
import styles from './enum.less';
class EnumSearch extends Component {
static getValue(filterEnum) {
if (isString(filterEnum)) {
return filterEnum;
} else {
return filterEnum ? filterEnum.value : null;
}
}
static getText(filterEnum) {
if (isString(filterEnum)) {
return filterEnum;
} else {
return filterEnum ? filterEnum.text : null;
}
}
constructor(props, context) {
super(props, context);
this.submit = this::this.submit;
this.reset = this::this.reset;
const checked = props.filterEnums.map(() => false);
this.state = {
checked,
};
}
submit() {
const { onSearchSubmit } = this.props;
const values = this.props.filterEnums
.filter((filterEnum, index) => this.state.checked[index])
.map(filterEnum => EnumSearch.getValue(filterEnum));
if (values && values.length === 1) {
onSearchSubmit(`=${values[0]}`);
} else if (values && values.length > 1) {
onSearchSubmit(`^${values.join(',')}`);
} else {
onSearchSubmit(null);
}
}
reset() {
this.setState(preState => ({
checked: preState.checked.map(() => false),
}));
}
render() {
const filterEnums = this.props.filterEnums;
return (
<div className={styles.container}>
{
filterEnums.map((filterEnum, index) => {
const onChange = (e) => {
this.setState((preState) => {
const checked = [...preState.checked];
checked[index] = e.target.checked;
return { checked };
});
};
return (
<span key={index}>
<Checkbox checked={this.state.checked[index]} onChange={onChange}>
{ EnumSearch.getText(filterEnum) }
</Checkbox>
<br />
</span>
);
}, this)
}
<div className={styles.buttonBar}>
<Button onClick={this.submit}>ok</Button>
<Button onClick={this.reset} className={styles.reset}>reset</Button>
</div>
</div>
);
}
}
EnumSearch.propTypes = {
filterEnums: PropTypes.arrayOf(PropTypes.shape({
text: PropTypes.string,
value: PropTypes.string,
})).isRequired,
onSearchSubmit: PropTypes.func,
};
const funcVoid = () => {};
EnumSearch.defaultProps = {
onSearchSubmit: funcVoid,
};
export default EnumSearch;
@import "index";
.container:extend(.popup) {}
.buttonBar {
margin-top: 8px;
.reset {
margin-left: 4px;
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { Icon } from 'antd';
import TextSearch from './text';
import DateSearch from './date';
import EnumSearch from './enum';
const Search = (props) => {
const { type, onSearchSubmit, filterEnums, children, ...rest } = props;
if (type === 'text') {
return (
<TextSearch onSearchSubmit={onSearchSubmit} {...rest}>
{ children }
</TextSearch>
);
} else if (type === 'date') {
return <DateSearch onSearchSubmit={onSearchSubmit} {...rest} />;
} else if (type === 'enum') {
return <EnumSearch filterEnums={filterEnums} onSearchSubmit={onSearchSubmit} {...rest} />;
} else {
return null;
}
};
Search.getIcon = (type, filtered) => {
const style = {
color: filtered ? '#108ee9' : '#aaa',
};
if (type === 'text') {
return (
<Icon type="search" style={style} />
);
} else if (type === 'date') {
return (
<Icon type="calendar" style={style} />
);
} else if (type === 'enum') {
return (
<Icon type="filter" style={style} />
);
} else {
return null;
}
};
Search.propTypes = {
onSearchSubmit: PropTypes.func,
};
export default Search;
.popup {
border-radius: 6px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
padding: 8px;
background-color: #fff;
}
.popupOk {
margin-left: 4px;
}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Input, Icon, Button } from 'antd';
import styles from './text.less';
class TextSearch extends Component {
constructor(props, context) {
super(props, context);
this.onChange = this::this.onChange;
this.bindRef = this::this.bindRef;
this.submit = this::this.submit;
this.emitEmpty = this::this.emitEmpty;
this.state = {
value: '',
};
}
onChange(e) {
const { onSearchPreChange, onSearchChanged } = this.props;
const value = onSearchPreChange(e.target.value);
this.setState({ value }, () => onSearchChanged(value));
}
bindRef(node) {
this.input = node;
}
submit() {
const { onSearchSubmit } = this.props;
const value = this.state.value;
const filter = value ? `@%${value}%` : null;
onSearchSubmit(filter);
}
emitEmpty() {
this.input.focus();
this.setState({
value: '',
});
}
render() {
const { onSearchPreChange, onSearchChanged, onSearchSubmit, ...rest } = this.props; // eslint-disable-line no-unused-vars
const props = {
suffix: this.state.value ? <Icon type="close" style={{ marginRight: '4px' }} onClick={this.emitEmpty} /> : null,
value: this.state.value,
onChange: this.onChange,
onPressEnter: this.submit,
ref: this.bindRef,
};
return (
<div className={styles.container}>
<div className={styles.inputWrapper}>
<Input {...rest} {...props} />
</div>
<Button className={styles.popupOk} onClick={this.submit}>ok</Button>
</div>
);
}
}
TextSearch.propTypes = {
onSearchPreChange: PropTypes.func,
onSearchChanged: PropTypes.func,
onSearchSubmit: PropTypes.func,
};
const funcIdentity = value => value;
const funcVoid = () => {};
TextSearch.defaultProps = {
onSearchPreChange: funcIdentity,
onSearchChanged: funcVoid,
onSearchSubmit: funcVoid,
};
export default TextSearch;
@import "index";
.container:extend(.popup) {}
.inputWrapper {
position: relative;
display: inline-block;
}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Input, Spin } from 'antd';
import { getCert, init, sign } from '../../utils/uca';
class UCA extends Component {
constructor(props, context) {
super(props, context);
this.state = {
ready: false,
};
}
componentDidMount() {
this.task = setInterval(() => {
const res = init();
if (res === 0) {
clearInterval(this.task);
this.task = null;
this.setState({
ready: true,
});
}
}, 300);
}
componentWillUnmount() {
if (this.task) {
clearInterval(this.task);
}
}
onChange = (value) => {
if (this.state.ready && this.props.onChange) {
this.props.onChange(this.valueToObj(value));
}
};
objToValue = (obj) => {
return obj ? obj.input : '';
};
valueToObj = (value) => {
if (!value) {
return value;
}
if (this.props.data) {
return sign(value, this.props.data);
} else {
return getCert(value);
}
};
render() {
const onChange = (e) => {
return this.onChange(e.target.value);
};
const { loading, value, style, ...rest } = this.props;
const { width, height } = style || {};
return (
<div style={{ width, height }}>
<Spin spinning={!this.state.ready || loading} size="small">
<Input {...rest} disabled={!this.state.ready || loading} value={this.objToValue(value)} onChange={onChange} type="password" />
</Spin>
</div>
);
}
}
UCA.propTypes = {
loading: PropTypes.bool,
data: PropTypes.string,
};
UCA.defaultProps = {
loading: false,
};
export default UCA;
/**
* Created by yaohx_169 on 2017/7/10.
*/
/**
* Created by yaohx_169 on 2017/7/10.
*/
import persistStore from 'redux-persist/lib/persistStore';
import { histories } from '../utils/auth';
const data = {
app: null,
};
export async function initApp(app) {
data.app = app;
await histories.init();
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/flatMap';
import { fetchMenus, fetchModuleInfos, fetchModuleLayout } from '../services/modules';
const data = {
flags: {
menus: false,
infos: false,
layout: {},
},
menus: [],
infos: [],
layout: {},
};
export default data;
// export const setMenus = (menus) => {
// data.init = true;
// data.menus = menus;
// };
const combineMenu = (menu1, menu2) => {
return {
...menu2,
children: combineMenus(menu1.children, menu2.children),
};
};
const combineMenus = (menus1, menus2) => {
const menus = [...menus1];
for (const menu of menus2) {
let find = false;
for (const i in menus) {
if (menus[i].name === menu.name) {
menus[i] = combineMenu(menus[i], menu);
find = true;
break;
}
}
if (!find) {
menus.push(menu);
}
}
return menus;
};
export const getMenus = async () => {
if (!data.flags.menus) {
const context = require.context('../register/modules', true, /^.*\.(js|jsx|json)/);
const staticMenus = flatMap(context.keys(), k => context(k).default);
const dynamicMenus = await fetchMenus();
data.menus = combineMenus(staticMenus, dynamicMenus);
data.flags.menus = true;
}
return data.menus;
};
export const invalidateMenus = () => {
data.flags.menus = false;
};
export const getModuleInfos = async () => {
if (!data.flags.infos) {
data.infos = await fetchModuleInfos();
data.flags.infos = true;
}
return data.infos;
};
export const getModuleInfo = async (name) => {
const infos = await getModuleInfos();
return (infos || []).filter(info => info.name === name).pop();
};
export const invalidateInfos = () => {
data.flags.infos = false;
};
export const getModuleLayout = async (name) => {
if (!data.flags.layout[name]) {
data.layout[name] = await fetchModuleLayout(name);
data.flags.layout[name] = true;
}
return data.layout[name];
};
export const invalidateLayout = (name) => {
delete data.flags.layout[name];
};
export const invalidateLayouts = () => {
data.flags.layout = {};
};
@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,
fieldset, input, textarea, p, blockquote, th, td {
margin: 0;
padding: 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
fieldset, img {
border: 0;
}
address, caption, cite, code, dfn, em, strong, th, var {
font-style: normal;
font-weight: normal;
}
ol, ul {
list-style: none;
}
caption, th {
text-align: left;
}
h1, h2, h3, h4, h5, h6 {
font-size: 100%;
font-weight: normal;
}
q:before, q:after {
content: '';
}
abbr, acronym {
border: 0;
}
li {
list-style: none;
}
input, img {
border: none;
}*/
:global(#root) {
margin: 0;
padding: 0;
height: 100%;
width: 100%;
}
.globalSpin {
width: 100%;
margin: 40px 0 !important;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<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>
<div id="root"></div>
<!--<script src="/index.js"></script>-->
</body>
</html>
/* eslint-disable no-underscore-dangle */
import dva from 'dva';
import createLoading from 'dva-loading';
import moment from 'moment';
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: getHistory({
basename: config.basename,
}),
onReducer: reducer => persistReducer(persistConfig, reducer),
onError(error) {
// eslint-disable-next-line no-console
processError(error);
},
});
app.model(appModel);
// 2. Plugins
app.use(createLoading({
effects: true,
}));
// 3. Model
// app.model(require('./models/example'));
// 4. Router
app.router(routerConfig);
// 5. Start
initApp(app).then(theApp => theApp.start('#root'));
import moment from 'moment';
import { split } from '../utils/filter';
import { is } from '../utils/helper';
const parseExpr = (valueExpr) => {
let valueExpr0 = valueExpr.replace(/\s+/, '');
let operator;
if (valueExpr0.indexOf('=') === 0) {
return {
operator: 'exact',
exact: valueExpr0.slice(1),
};
} else if (valueExpr0.indexOf('~') === 0) {
operator = 'range';
const ret = {
operator,
};
valueExpr0 = valueExpr0.slice(1).replace(/\s+/, '');
ret.downClose = valueExpr0[0] === '[';
valueExpr0 = valueExpr0.slice(1).replace(/\s+/, '');
let pos = valueExpr0.indexOf(',');
if (pos === -1) {
throw new Error('Invalid input.');
}
ret.down = Number.parseFloat(valueExpr0.slice(0, pos));
valueExpr0 = valueExpr0.slice(pos + 1);
pos = valueExpr0.indexOf(']');
if (pos !== -1) {
ret.upClose = true;
ret.up = Number.parseFloat(valueExpr0.slice(0, pos));
return ret;
}
pos = valueExpr0.indexOf(')');
if (pos !== -1) {
ret.upClose = false;
ret.up = Number.parseFloat(valueExpr0.slice(0, pos));
return ret;
}
throw new Error('Invalid input.');
} else if (valueExpr0.indexOf('@') === 0) {
operator = 'like';
return {
operator,
like: valueExpr0.slice(1),
};
} else if (valueExpr0.indexOf('^') === 0) {
operator = 'list';
return {
operator,
list: valueExpr0.slice(1).split(','),
};
} else {
throw new Error('Invalid input.');
}
};
const toFunction = (keysExpr, valueExpr) => {
const parsedExpr = parseExpr(valueExpr);
return (obj) => {
const value = obj[keysExpr.slice(2)]; // 略过'f-'
if (value === undefined) {
return true;
}
if (value === null) {
return parsedExpr.operator === 'exact' && parsedExpr.exact === '' || parsedExpr.exact === null;
}
if (parsedExpr.operator === 'exact') {
if (Number.isFinite(value)) {
return value === Number.parseFloat(parsedExpr.exact);
}
if (moment.isMoment(value)) {
return value.isSame(Number.parseFloat(parsedExpr.exact));
}
if (moment.isDate(value)) {
return value.isSame(Number.parseFloat(parsedExpr.exact));
}
if (is(value, 'String')) {
return value === parsedExpr.exact;
}
} else if (parsedExpr.operator === 'range') {
if (Number.isFinite(value)) {
let ret = true;
if (!(/^\s*$/).test(parsedExpr.down)) {
if (parsedExpr.downClose) {
ret = ret && value >= Number.parseFloat(parsedExpr.down);
} else {
ret = ret && value > Number.parseFloat(parsedExpr.down);
}
}
if (!(/^\s*$/).test(parsedExpr.up)) {
if (parsedExpr.upClose) {
ret = ret && value <= Number.parseFloat(parsedExpr.up);
} else {
ret = ret && value < Number.parseFloat(parsedExpr.up);
}
}
return ret;
} else if (moment.isDate(value) || moment.isMoment(value) || is(value, 'String')) {
let ret = true;
if (!(/^\s*$/).test(parsedExpr.down)) {
const a = moment(value);
const b = moment(Number.parseFloat(parsedExpr.down));
if (parsedExpr.downClose) {
ret = ret && (a.isAfter(b) || a.isSame(b));
} else {
ret = ret && a.isAfter(b);
}
}
if (!(/^\s*$/).test(parsedExpr.up)) {
const a = moment(value);
const b = moment(Number.parseFloat(parsedExpr.up));
if (parsedExpr.upClose) {
ret = ret && (a.isBefore(b) || a.isSame(b));
} else {
ret = ret && a.isBefore(b);
}
}
return ret;
}
} else if (parsedExpr.operator === 'like') {
if (is(value, 'String')) {
const reg = new RegExp(`^${parsedExpr.like
.replace(/%/g, '[\\s\\S]*')
.replace(/_/g, '[\\s\\S]')
.replace(/\[!([\s\S]+)]/, '[^$1]')}$`);
return reg.test(value);
}
} else if (parsedExpr.operator === 'list') {
for (const test of parsedExpr.list) {
// noinspection EqualityComparisonWithCoercionJS
if (test == value) { // eslint-disable-line eqeqeq
return true;
}
}
return false;
}
throw new Error(`Invalid input.${keysExpr}|${valueExpr}.`);
};
};
const toFilters = (keysExpr, valueExpr) => {
const keys = split(keysExpr);
const values = split(valueExpr);
if (keys.length !== values.length) {
throw new Error('Invalid input.');
}
return (obj) => {
const len = keys.length;
const fucs = [];
for (let i = 0; i < len; ++i) {
const key = keys[i];
const value = values[i];
fucs.push(toFunction(key, value));
}
for (let i = 0; i < len; ++i) {
if (fucs[i](obj)) {
return true;
}
}
return false;
};
};
export default toFilters;
import Mock from 'mockjs';
const Random = Mock.Random;
const padDigits = (number, digits) => {
return new Array(Math.max(digits - String(number).length + 1, 0)).join('0') + number;
};
let baseId = 10000;
let groupIdx = 1;
let moduleIdx = 1;
const groupPool = [];
const groupIdGenerator = () => {
const id = baseId++;
groupPool.push({
id,
children: [],
});
return id;
};
const groupNameGenerator = () => `模块组${padDigits(groupIdx++, 2)}`;
const moduleIdGenerator = () => baseId++;
const moduleNameGenerator = () => `模块${padDigits(moduleIdx++, 2)}`;
// eslint-disable-next-line func-names
const parentPicker = isRoot => function () {
if (!isRoot) {
const parent = Random.pick(groupPool);
parent.children.push(this.id);
return Number.parseInt(parent.id, 10);
} else {
return null;
}
};
const moduleCreator = () => {
const leafGroupPool = groupPool.filter(group => group.children.length === 0);
const modules0 = Mock.mock({
[`modules|${leafGroupPool.length}-${leafGroupPool.length}`]: [{
id: moduleIdGenerator,
name: moduleNameGenerator,
'parent|+1': leafGroupPool.map(group => group.id),
}],
}).modules;
const modules1 = Mock.mock({
[`modules|0-${3 * groupPool.length}`]: [{
id: moduleIdGenerator,
name: moduleNameGenerator,
'parent|1': groupPool.map(group => group.id),
}],
}).modules;
return [
...modules0,
...modules1,
];
};
const defaultGroups = [{
id: 1,
name: '业务流程',
parent: null,
group: true,
}, {
id: 2,
name: '业务流程1',
parent: 1,
group: true,
}];
const defaultModules = [{
id: 1000,
name: '当前任务',
parent: 1,
route: 'task',
}, {
id: 1001,
name: '当前任务1',
parent: 2,
route: 'task',
}];
export function genModules() {
const mock = Mock.mock({
'groups|3-5': [{
name: groupNameGenerator,
parent: parentPicker(true),
id: groupIdGenerator,
group: true,
}],
'subGroups|0-15': [{
name: groupNameGenerator,
parent: parentPicker(false),
id: groupIdGenerator,
group: true,
}],
modules: moduleCreator,
});
const mNum = mock.modules.length;
return {
all: [
...defaultGroups,
...mock.groups,
...mock.subGroups,
...mock.modules,
...defaultModules,
],
public: [...defaultModules, ...Random.pick(mock.modules, (mNum / 3) | 0, (2 * mNum / 3) | 0)],
};
}
import Mock from 'mockjs';
import moment from 'moment';
import flatMap from 'lodash/flatMap';
const defaultDateFormat = 'YYYY-MM-DD';
const defaultTimeFormat = 'HH:mm:ss';
const defaultDateTimeFormat = `${defaultDateFormat} ${defaultTimeFormat}`;
const Random = Mock.Random;
export function getTasks() {
const states = ['状态1', '状态2', '状态3', '状态4', '状态5', '状态5'];
const start = Random.natural(0, 100);
const end = Random.natural(start + 5, start + 20);
const progresses = Random.range(start, end).map(idx => ({
id: idx,
name: `流程${idx}`,
}));
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) => {
const dateOffset = {
days: Random.natural(0, 60),
hours: Random.natural(0, 23),
minutes: Random.natural(0, 59),
seconds: Random.natural(0, 59),
milliseconds: Random.natural(0, 999),
};
const deadLineOffset = {
days: Random.natural(0, 180),
hours: Random.natural(0, 23),
minutes: Random.natural(0, 59),
seconds: Random.natural(0, 59),
milliseconds: Random.natural(0, 999),
};
const date = moment().subtract(dateOffset);
const deadline = date.add(deadLineOffset);
return {
pId: progress.id,
pName: progress.name,
nId: idx,
nName: `任务${idx}`,
state: Random.pick(states),
date: date.format(defaultDateTimeFormat),
deadline: deadline.format(defaultDateTimeFormat),
};
});
});
return Random.shuffle(tasks);
}
export default {
namespace: 'app',
state: {},
subscriptions: {
setup({ dispatch, history }) { // eslint-disable-line
},
},
effects: {
},
reducers: {
},
};
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 { encrypt } from '../utils/helper';
import { setToken, setUser, histories } from '../utils/auth';
import { errors } from '../utils/error';
import config from '../utils/config';
import { getStore } from '../data/app';
const successAuthed = async (tokenId, userName, remember) => {
await histories.pushHistory('userName', null, remember ? userName : '');
await setToken(tokenId);
const uInfo = await userInfo();
await setUser(uInfo.id, uInfo.name);
const latest = await histories.getLatest('module', uInfo.id);
if (latest && config.fastNavigationPage) {
getStore().dispatch(routerRedux.push('/fastNav'));
} else {
getStore().dispatch(routerRedux.push('/main'));
}
};
const processAuthRequirements = (requirements, supports) => {
let res = [];
for (const requirement of requirements) {
const { authTypes } = requirement;
if (authTypes && authTypes.length > 0) {
const filteredAuthTypes = authTypes.filter(authType => supports.indexOf(authType) !== -1);
if (filteredAuthTypes.length === 0) {
throw errors.unsupportedAuthType(authTypes);
}
res = res.map(require => require.filter(item => authTypes.indexOf(item) === -1)).filter(require => require.length > 0);
res.push(filteredAuthTypes);
}
}
return res;
};
let tkId;
export default {
namespace: 'login',
state: {
status: 'login',
userName: '',
ucaCode: '',
authRequires: [],
},
reducers: {
setStatus(state, { payload }) {
return {
...state,
status: payload,
};
},
setUserName(state, { payload }) {
return {
...state,
userName: payload,
};
},
setUCACode(state, { payload }) {
return {
...state,
ucaCode: payload,
};
},
setAuthRequires(state, { payload }) {
return {
...state,
authRequires: payload,
};
},
},
effects: {
*init(ignored, { put, call }) {
yield put({ type: 'setStatus', payload: 'login' });
yield put({ type: 'setUCACode', payload: '' });
yield put({ type: 'setAuthRequires', payload: [] });
const userName = yield call(histories.getLatest, 'userName', null);
yield put({ type: 'setUserName', payload: userName || '' });
},
*login({ payload: userName }, { call, put }) {
const { tokenId, remainedAuthRequirements } = yield call(login, {
type: 'userName',
data: userName,
});
tkId = tokenId;
const { requirements } = remainedAuthRequirements;
const requires = processAuthRequirements(requirements, ['password', 'uca']);
if (requires.length === 0) {
yield call(successAuthed, tokenId);
} else {
if (requires.some(req => req.indexOf('uca') !== -1)) {
yield put({ type: 'requestUCACode' });
}
yield put({ type: 'setAuthRequires', payload: requires });
}
yield put({ type: 'setStatus', payload: 'auth' });
},
*requestUCACode(ignored, { call, put }) {
const { data } = yield call(authorize, yield call(requestCode, encrypt(tkId)));
yield put({ type: 'setUCACode', payload: data });
},
*auth({ payload: { userName, password, uca, remember } }, { call }) {
let response;
if (password) {
response = yield call(authorize, yield call(passValidate, password, encrypt(tkId)));
const { status } = response;
if (status !== 'authed' && status !== 'skipped') {
throw errors.wrongPassword();
}
}
if (uca) {
if (uca.status === 0) {
response = yield call(authorize, yield call(ucaValidate, uca.signed, encrypt(tkId)));
const { status } = response;
if (status !== 'authed' && status !== 'skipped') {
throw errors.generalError('证书验证失败。');
}
} else {
throw errors.generalError(uca.error);
}
}
if (response) {
const { status, remainedAuthRequirements, data } = response;
if (status !== 'authed' && status !== 'skipped') {
throw errors.authFailed(data);
}
if (remainedAuthRequirements.requirements.length === 0) {
yield call(successAuthed, tkId, userName, remember);
}
}
},
},
subscriptions: {},
};
import { routerRedux } from 'dva/router';
import isString from 'lodash/isString';
import { logout } from '../../services/main';
import { getMenus, getModuleInfo } from '../../data/modules';
import { delToken, getUser, delUser } from '../../utils/auth';
const createMenu = async (configure) => {
const menu = {
name: configure.name,
icon: configure.icon,
text: configure.showName,
};
if (!menu.children) {
menu.children = [];
}
if (configure.children) {
for (const child of configure.children) {
if (child) {
menu.children.push(await createMenu(child));
}
}
}
if (configure.modules) {
for (const module of configure.modules) {
let info = module;
if (isString(module)) {
info = await getModuleInfo(module);
}
if (info) {
menu.children.push({
name: info.name,
icon: info.icon,
text: info.showName,
});
}
}
}
return menu;
};
const makeIcon = (menu) => {
const children = menu.children;
if (!children || children.length === 0) {
if (!menu.icon) {
menu.icon = 'file'; // eslint-disable-line no-param-reassign
}
} else {
if (!menu.icon) {
menu.icon = 'folder'; // eslint-disable-line no-param-reassign
}
for (const child of children) {
makeIcon(child);
}
}
};
const createMenus = async (menusConfigure) => {
const menus = [];
if (menusConfigure) {
for (const configure of menusConfigure) {
menus.push(await createMenu(configure));
}
}
for (const menu of menus) {
makeIcon(menu);
}
return menus;
};
export default {
namespace: 'main',
state: {
user: '',
menus: [],
},
subscriptions: {
setup({ dispatch, history }) { // eslint-disable-line
},
},
effects: {
*fetchUser(ignored, { put, call }) {
const user = yield call(getUser);
if (!user) {
yield put(routerRedux.push('/login'));
}
yield put({ type: 'queryUserSuccess', payload: user.name });
},
*fetchModules(ignored, { put, call }) {
const configures = yield call(getMenus);
const menus = yield call(createMenus, configures);
yield put({ type: 'queryMenusSuccess', payload: menus });
},
*logout(ignored, { put, call }) {
yield call(logout);
yield call(delToken);
yield call(delUser);
yield put(routerRedux.push('/login'));
},
},
reducers: {
queryUserSuccess(state, { payload: user }) {
return {
...state,
user,
};
},
queryMenusSuccess(state, { payload: menus }) {
return {
...state,
menus,
};
},
},
};
export default {
namespace: 'apiDoc',
state: {},
reducers: {},
effects: {},
subscriptions: {},
};
import { push } from '../../../../services/route';
import { getAppInfoes, addApp, editApp, removeApp, deployApp, undeployApp, editDeployment } from '../../../../services/app';
import { getToken } from '../../../../utils/auth';
export default {
namespace: 'appInfo',
state: {
allAppInfo: [],
token: '',
uploadURL: '',
record: {},
},
subscriptions: {
setup({ dispatch }) { // eslint-disable-line
},
},
effects: {
*getTokens(action, { put, call }) {
const token = yield call(getToken);
yield put({ type: 'queryToken', payload: token });
},
*getAppInfo({ payload: { name } }, { put, call }) {
const info = yield call(getAppInfoes, name);
yield put({ type: 'queryAppInfo', payload: info });
},
*addAppInfo({ payload: { values } }, { call }) {
yield call(addApp, values.name, values.packageName, values.description);
push('../list');
},
*editAppInfo({ payload: { values } }, { call }) {
yield call(editApp, values.name, values.newName, values.packageName, values.description);
push('../list');
},
*delAppInfo({ payload: { name } }, { call, put }) {
yield call(removeApp, name);
yield put({ type: 'getAppInfo', payload: { } });
push('../list');
},
*uploadAPK({ payload: { response } }, { put }) {
if (response.errorCode !== 0) {
throw new Error(response.message);
}
const uploadURL = response.data[0];
yield put({ type: 'queryUploadURL', payload: uploadURL });
},
*addDeployApp({ payload: { values } }, { call, put }) {
yield call(deployApp, values.idOrName, values.uri, values.description, values.release);
const name = values.idOrName;
yield put({ type: 'getAppInfo', payload: { name } });
},
*removeDeployApp({ payload: { id, name } }, { call, put }) {
yield call(undeployApp, id);
yield put({ type: 'getAppInfo', payload: { name } });
},
*editSaveDeploy({ payload: { values, name } }, { call, put }) {
yield call(editDeployment, values.id, values.description, values.status);
yield put({ type: 'getAppInfo', payload: { name } });
},
},
reducers: {
queryUploadURL(state, { payload: uploadURL }) {
return {
...state,
uploadURL,
};
},
queryAppInfo(state, { payload: allAppInfo }) {
return {
...state,
allAppInfo,
};
},
queryToken(state, { payload: token }) {
return {
...state,
token,
};
},
saveRecord(state, { payload: record }) {
return {
...state,
record,
};
},
},
};
export default (info, layout) => ({
namespace: 'task',
state: {
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;
/**
* Created by zhouhuan on 2017/11/20.
*/
export default [
{
name: 'appManagementInfo',
showName: 'App管理',
modules: [
{
name: 'appInfo',
showName: 'app信息',
layout: {
route: 'appManagement',
},
},
],
},
];
/* eslint-disable */
export default [/*{
name: 'test-menu',
showName: '测试',
modules: [{
name: 'test-module',
showName: '测试',
layout: {
route: 'api-doc',
},
}],
}*/];
/* eslint-disable no-underscore-dangle */
import React from 'react';
import PropTypes from 'prop-types';
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 { 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';
import styles from './index.css';
const Loading = <Spin size="large" className={styles.globalSpin} />;
const registerModel = (app, model) => {
// noinspection JSUnresolvedVariable
if (!(app._models.filter(m => m.namespace === model.namespace).length === 1)) {
app.model(model);
}
};
const maybeLogin = async (replace) => {
if (!(await isAuthed())) {
return replace('/login');
}
};
const authenticated = async (replace) => {
await maybeLogin(replace);
};
const combinePath = (base, path) => {
if (!base) {
return path;
}
if (base[base.length - 1] === '/') {
return `${base}${path}`;
} else {
return `${base}/${path}`;
}
};
const createRoute = async (app, group, basePath) => {
const { name, showName, modules, children } = group;
const theFullPath = combinePath(basePath, name);
// noinspection JSUnusedLocalSymbols
return {
path: name,
fullPath: theFullPath,
name: showName,
component: Monk,
getChildRoutes(nextState, cb) {
createRoutes(app, modules, children, theFullPath)
.then((result) => {
cb(null, result);
})
.catch((err) => {
cb(err, null);
});
},
};
};
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 = [];
if (modules) {
for (const module of modules) {
let info;
let layout;
if (isString(module)) {
info = await getModuleInfo(module);
layout = await getModuleLayout(module);
} else {
info = module;
layout = module.layout;
}
const { name, showName } = info;
const route = {
path: name,
fullPath: combinePath(basePath, name),
name: showName,
};
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];
}
}
}
} 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) => {
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);
} else {
resolve(res);
}
});
}))
.then(() => cb())
.catch(err => cb(err));
};
} else {
route.onEnter = (nextState, replace, cb) => {
getUser()
.then(u => u.id)
.then(uid => moduleEnterHook(app, uid, infoEx))
.then(() => cb())
.catch(err => cb(err));
};
}
routes.push(route);
}
}
if (groups) {
for (const group of groups) {
routes.push(await createRoute(app, group, basePath));
}
}
return routes;
};
function RouterConfig({ history, app }) {
const routes = [
{
path: '/',
component: App,
indexRoute: {
onEnter: (nextState, replace, cb) => {
getUser()
.then(u => u && u.id)
.then(uid => histories.getLatest('module', uid))
.then((latest) => {
if (latest && config.fastNavigationPage) {
replace('/fastNav');
} else {
replace('/main');
}
cb();
})
.catch((err) => {
cb(err);
});
},
},
childRoutes: [
{
path: 'login',
getComponent(ignored, cb) {
Promise.all([
import('./models/login'),
import('./routes/login'),
]).then(([model, route]) => {
registerModel(app, model.default);
cb(null, route.default);
});
},
},
{
path: 'main',
onEnter: (ignored, replace, cb) => {
authenticated(replace).then(() => cb()).catch(err => cb(err));
},
getComponent(ignored, cb) {
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, '/main')
.then((result) => {
cb(null, result);
})
.catch((err) => {
cb(err, null);
});
})
.catch((err) => {
cb(err, null);
});
},
},
],
},
];
if (config.fastNavigationPage) {
routes[0].childRoutes.push({
path: 'fastNav',
onEnter: (ignored, replace, cb) => {
authenticated(replace).then(() => cb()).catch(err => cb(err));
},
getComponent(ignored, cb) {
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.isRequired,
app: PropTypes.object.isRequired,
};
export default RouterConfig;
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'dva';
import styles from './app.less';
const App = ({ children }) => {
return (
<div className={styles.app}>{children || null}</div>
);
};
App.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
};
export default connect()(App);
.app {
width: 100%;
height: 100%;
position: relative;
border: 0;
margin: 0;
padding: 0;
}
import React from 'react';
import { Input, Select } from 'antd';
import styles from './authInput.less';
class AuthInputs extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
index: props.data.length > 0 ? 0 : -1,
};
}
onChange = (value) => {
this.setState({
index: this.props.data.findIndex(item => item.key === value),
});
};
render() {
const { data } = this.props;
const select = this.state.index >= 0 ? data[this.state.index].key : undefined;
if (data.length > 0) {
return (
<Input.Group compact className={styles['bl-authInput']}>
<Select value={select} onChange={this.onChange} style={{ width: '30%' }}>
{
data.map(item => (<Select.Option value={item.key} key={item.key}> { item.label } </Select.Option>))
}
</Select>
{
React.cloneElement(data[this.state.index >= 0 ? this.state.index : 0].node(),
{
style: { width: '70%' },
})
}
</Input.Group>
);
}
}
}
export default AuthInputs;
.bl-authInput > * {
vertical-align: middle!important;
}
/**
* Created by yaohx_169 on 2017/6/5.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { Form, Icon, Input, Checkbox, Button } from 'antd';
import { connect } from 'dva';
import AuthInputs from './authInput';
import UCA from '../../components/uca';
import config from '../../utils/config';
import styles from './index.less';
class LoginForm extends React.Component {
componentDidMount() {
this.props.dispatch({ type: 'login/init' });
}
onUserBlur = (e) => {
const { value } = e.target;
if (value) {
this.props.dispatch({ type: 'login/login', payload: value });
}
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
if (this.props.login.status === 'login') {
this.authFocus = true;
this.props.dispatch({ type: 'login/login', payload: values.userName });
} else if (this.props.login.status === 'auth') {
this.props.dispatch({ type: 'login/auth', payload: values });
}
}
});
};
createPassword = (focus = false) => {
const { getFieldDecorator } = this.props.form;
return getFieldDecorator('password', {
rules: [{
required: true,
message: '请输入密码。',
}],
})(
<Input autoFocus={focus} prefix={<Icon type="lock" />} type="password" placeholder="密码" />,
);
};
createUCA = (focus = false) => {
const { form, login } = this.props;
const { getFieldDecorator } = form;
return getFieldDecorator('uca', {
rules: [{
required: true,
message: '请输入usb-key的密钥。',
}],
})(
<UCA autoFocus={focus} loading={!login.ucaCode} data={login.ucaCode} prefix={<Icon type="hdd" placeholder="密钥" />} />,
);
};
createValidates = () => {
const { login } = this.props;
if (login.status === 'login') {
return [];
} else if (login.status === 'auth') {
const { authRequires } = login;
return authRequires.map((authRequire) => {
const pass = authRequire.indexOf('password') !== -1;
const uca = authRequire.indexOf('uca') !== -1;
let focus = false;
if (pass || uca) {
if (this.authFocus) {
focus = true;
this.authFocus = false;
}
}
if (pass && uca) {
return (
<Form.Item key="passOrUca">
<AuthInputs
data={[
{
key: 'password',
label: '密码',
node: () => this.createPassword(focus),
},
{
key: 'uca',
label: '证书',
node: () => this.createUCA(focus),
},
]}
/>
</Form.Item>
);
} else if (pass) {
return (
<Form.Item key="pass">
{ this.createPassword(focus) }
</Form.Item>
);
} else if (uca) {
return (
<Form.Item key="uca">
{ this.createUCA(focus) }
</Form.Item>
);
} else {
return <Form.Item />;
}
});
}
};
render() {
const { getFieldDecorator } = this.props.form;
const { loading } = this.props;
return (
<div className={styles.canvas}>
<div className={styles.container}>
<div className={styles.logo}>
<img alt="logo" src={config.logo} />
<span>{config.name}</span>
</div>
<Form onSubmit={this.handleSubmit}>
<Form.Item>
{
getFieldDecorator('userName', {
initialValue: this.props.login.userName,
rules: [{
required: true,
message: '请输入用户名。',
}],
})(
<Input
autoFocus={!this.authFocus}
onBlur={this.onUserBlur}
prefix={<Icon type="user" />}
placeholder="用户名"
onChange={() => {
this.props.dispatch({ type: 'login/setStatus', payload: 'login' });
}}
/>,
)
}
</Form.Item>
{
this.createValidates()
}
<Form.Item>
{
getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(
<Checkbox>记住我</Checkbox>,
)
}
<a className={styles.forgot} href="">忘记密码</a>
<Button type="primary" htmlType="submit" className={styles.submit} loading={loading}>
登录
</Button>
或者<a>现在注册!</a>
</Form.Item>
</Form>
</div>
</div>
);
}
}
LoginForm.propTypes = {
form: PropTypes.object,
loading: PropTypes.bool,
dispatch: PropTypes.func,
};
const mapStateToProps = ({ login, loading }) => {
return {
login,
loading: loading.effects['login/login'],
};
};
export default connect(mapStateToProps)(Form.create()(LoginForm));
.canvas {
width: 100%;
height: 100%;
position: relative;
background-color: #555555;
}
.container {
width: 320px;
position: absolute;
background-color: white;
top: 50%;
left: 50%;
padding: 36px;
margin: -160px 0 0 -160px;
box-shadow: 0 0 100px rgba(0,0,0,.08);
}
.logo {
text-align: center;
height: 40px;
line-height: 40px;
cursor: pointer;
margin-bottom: 24px;
img {
width: 40px;
margin-right: 8px;
}
span {
vertical-align: text-bottom;
font-size: 16px;
text-transform: uppercase;
display: inline-block;
}
}
.forgot {
float: right;
}
.submit {
width: 100%;
}
/* 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 styles from './header.less';
import { makePath } from '../../utils/helper';
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() {
const { dispatch } = this.props;
dispatch({ type: 'main/fetchUser' });
dispatch({ type: 'main/fetchDomain' });
dispatch({ type: 'main/fetchDomains' });
}
render() {
const { dispatch, user, routes, params } = this.props;
const nRoutes = normRoutes(routes);
const userTitle = (
<span>
<Icon type="user" />
{ user }
</span>
);
const onClick = ({ keyPath }) => {
if (keyPath[1] === 'user' && keyPath[0] === 'logout') {
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: nRoutes,
params,
itemRender(route, _params, _routes, paths) {
if (!paths || !paths.length) {
return null;
}
if (paths.length === 1) {
return <Icon type="home" />;
}
const bread = route.name ? route.name : route.path;
return <Link to={joinPath(paths)}>{ bread }</Link>;
},
};
const menuProps = {
className: styles.menu,
mode: 'horizontal',
theme: 'light',
selectable: false,
onClick,
};
return (
<div className={styles.board}>
<Breadcrumb {...breadsProps} />
<Menu {...menuProps}>
<SubMenu title={userTitle} key="user">
<Menu.Item key="logout">
<span>
<Icon type="logout" />
登出
</span>
</Menu.Item>
</SubMenu>
</Menu>
</div>
);
}
}
HeaderPane.propTypes = {
dispatch: PropTypes.func,
user: PropTypes.string,
};
const mapStateToProps = ({ main }) => {
return {
user: main.user,
};
};
export default connect(mapStateToProps)(HeaderPane);
.board {
position: absolute;
width: 100%;
bottom: 0;
background-color: #fff;
.breads {
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 { 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';
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 {
constructor(props, context) {
super(props, context);
this.onClick = this::this.onClick;
this.onCollapse = this::this.onCollapse;
this.state = {
collapsed: false,
mode: 'inline',
};
}
componentDidMount() {
this.props.dispatch({ type: 'main/fetchModules' });
}
onClick({ keyPath }) {
const paths = keyPath.reverse().join('/');
thisPush(this, `/main/${paths}`);
// this.props.dispatch(routerRedux.push(fullPath(`/main/${paths}`)));
}
onCollapse(collapsed) {
this.setState({
collapsed,
mode: collapsed ? 'vertical' : 'inline',
});
}
render() {
const { main, children } = this.props;
const { menus } = main;
const mode = this.state.mode;
const menuProps = {
mode,
menus,
onClick: this.onClick,
};
const { routes, params } = this.props;
const headerProps = {
routes,
params,
menus,
};
return (
<Layout className={styles.layout}>
<Sider collapsible collapsed={this.state.collapsed} onCollapse={this.onCollapse}>
<SiderLogo />
<SiderMenu {...menuProps} />
</Sider>
<Layout>
<Header>
<HeaderPane {...headerProps} />
</Header>
<ContentWithHeight>
{ children || null }
</ContentWithHeight>
<Footer>
<span className={styles.company}>®上海铂蓝信息科技有限公司</span>
</Footer>
</Layout>
</Layout>
);
}
}
Main.propTypes = {
children: PropTypes.oneOfType([PropTypes.element, PropTypes.arrayOf(PropTypes.element)]),
main: PropTypes.object,
};
export default connect(({ main }) => ({ main }))(withRouter(Main));
.layout {
height: 100%;
:global .ant-layout-header {
position: relative;
}
:global .ant-layout-content {
position: relative;
overflow: auto;
}
:global .ant-layout-footer {
background-color: #fff;
:local .company {
float: right;
margin-bottom: -12px;
margin-right: -24px;
}
}
}
# 登录与认证
登录分为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 React from 'react';
import { Button, Input, Form } from 'antd';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const FormItem = Form.Item;
class Add extends React.Component {
componentDidMount() {
}
onCancel = () => {
thisPush(this, { pathname: '../list' });
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.dispatch({ type: 'appInfo/addAppInfo', payload: { values } });
}
});
};
render() {
// console.log(this.props.appInfo);
const { getFieldDecorator } = this.props.form;
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Form onSubmit={this.handleSubmit}>
<FormItem
label="名称"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('name', {
rules: [{ required: true, message: 'Please input your name!' }],
})(
<Input />,
)}
</FormItem>
<FormItem
label="包名"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('packageName', {
rules: [{ required: true, message: 'Please input your packageName!' }],
})(
<Input />,
)}
</FormItem>
<FormItem
label="描述"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('description', {
})(
<Input />,
)}
</FormItem>
<FormItem
wrapperCol={{ span: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
提交
</Button>
<Button style={{ marginLeft: 60 }} onClick={this.onCancel}>
返回
</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
export default Form.create()(Add);
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 18px;
}
.container {
margin-top: 10px;
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
.divBtn{
margin-top: 10px;
Button{
margin-left: 10px;
}
}
.divRow{
margin-bottom: 10px;
width: 300px;
}
.divCode{
border: 1px solid;
border-color:#C4C4C4;
padding: 20px;
border-radius: 10px;
}
/**
* Created by zhouhuan on 2017/11/21.
*/
import React from 'react';
import { uniqBy } from 'lodash';
import { Button, Input, Upload, Form, message, Icon, Modal, Radio } from 'antd';
import { encrypt } from '../../../../utils/helper';
import config from '../../../../utils/config';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const { TextArea } = Input;
const RadioGroup = Radio.Group;
class AddDeploy extends React.Component {
state = {
descriptionInfo: '',
radioValue: false,
};
componentDidMount() {
this.props.dispatch({ type: 'appInfo/getTokens' });
}
onChangeInfo = (e) => {
this.setState({ descriptionInfo: e.target.value });
};
onCancel = () => {
const { value } = this.props.location.state;
thisPush(this, { pathname: '../deploy', state: { value } });
};
onChange = (e) => {
this.setState({
radioValue: e.target.value,
});
};
onSubmit = () => {
const { descriptionInfo, radioValue } = this.state;
const { value } = this.props.location.state;
const { uploadURL } = this.props.appInfo;
const name = value.name;
if (uploadURL === '') {
Modal.error({
title: '请先上传Apk!',
});
} else {
const values = { idOrName: name, uri: uploadURL, description: descriptionInfo, release: radioValue };
this.props.dispatch({ type: 'appInfo/addDeployApp', payload: { values } });
thisPush(this, { pathname: '../deploy', state: { value } });
}
};
render() {
const { token, uploadURL } = this.props.appInfo;
// console.log(token);
// console.log(uploadURL);
const tokens = encrypt(token);
// let URL;
// if (uploadURL !== '') {
// URL = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(uploadURL)}`;
// }
let action;
if (uploadURL) {
action = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(uploadURL)}`;
} else {
action = `${config.apiContextPath}/resource/${tokens}`;
}
const props = {
name: 'test',
accept: '.apk',
action,
headers: {
authorization: 'authorization-text',
},
fileList: this.state.fileList,
data: (file) => {
return file;
},
onChange: (info) => {
if (info.file.status !== 'uploading') {
// console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
message.success(`${info.file.name} file uploaded successfully`);
const response = info.file.response;
this.props.dispatch({ type: 'appInfo/uploadAPK', payload: { response } });
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
let fileList = info.fileList;
fileList = fileList.slice(-3);
fileList = fileList.filter((file) => {
if (file.response) {
return file.response.errorCode === 0;
}
return true;
});
fileList = fileList.map((file) => {
if (file.response) {
// Component will show file.url as link
// eslint-disable-next-line
file.url = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(file.response.data[0])}`;
}
return file;
});
fileList = uniqBy(fileList, (file) => {
const url = file.url;
return url ? url.slice(url.lastIndexOf('/')) : null;
});
this.setState({
fileList,
});
},
};
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.divRow}>
<TextArea rows={5} placeholder="description" onChange={this.onChangeInfo} />
</div>
<div className={styles.divRow}>
<RadioGroup onChange={this.onChange} value={this.state.radioValue}>
<Radio value={false}>development</Radio>
<Radio value>release</Radio>
</RadioGroup>
</div>
<div className={styles.divBtn}>
<Button type="primary" icon="check" onClick={this.onSubmit}>提交</Button>
<Button type="primary" icon="close" onClick={this.onCancel}>取消</Button>
<Upload {...props}>
<Button type="primary">
<Icon type="upload" /> 选择上传
</Button>
</Upload>
</div>
</div>
</div>
);
}
}
export default Form.create()(AddDeploy);
/* eslint-disable no-undef,jsx-a11y/img-has-alt */
/**
* Created by zhouhuan on 2017/11/21.
*/
import React from 'react';
import QRcode from 'qrcode';
import { Button, Table, Popconfirm, Modal } from 'antd';
import config from '../../../../utils/config';
import { encrypt } from '../../../../utils/helper';
import styles from './list.less';
import { thisPush } from '../../../../services/route';
class Deploy extends React.Component {
static makeColumns() {
return [{
title: 'ID',
dataIndex: 'id',
}, {
title: '更新时间',
dataIndex: 'updateTime',
}, {
title: '状态',
dataIndex: 'status',
}, {
title: '版本号',
dataIndex: 'versionNumber',
}, {
title: '描述',
dataIndex: 'description',
}, {
title: 'uri',
dataIndex: 'uri',
}, {
title: '操作',
dataIndex: 'operation',
render: (text, record, index) => (
<span>
<span className="ant-divider" />
<a onClick={() => this.onEdit(record, index)}>Edit</a>
<span className="ant-divider" />
<Popconfirm title="确定删除?" okText="Yes" cancelText="No" onConfirm={() => this.onDelete(record, index)}>
<a>Delete</a>
</Popconfirm>
<span className="ant-divider" />
<a onClick={() => this.onQrcode(record, index)}>qrcode</a>
</span>
),
}];
}
state = {
urls: '',
visible: false,
completeUrl: '',
};
componentDidMount() {
this.props.dispatch({ type: 'appInfo/getTokens' });
const { value } = this.props.location.state;
// console.log(value);
this.props.dispatch({ type: 'appInfo/getAppInfo', payload: { name: value.name } });
}
onDelete = (record) => {
const id = record.id;
const { value } = this.props.location.state;
this.props.dispatch({ type: 'appInfo/removeDeployApp', payload: { id, name: value.name } });
};
onEdit = (record) => {
const { value } = this.props.location.state;
thisPush(this, { pathname: '../editDeploy', state: { record, value } });
};
onQrcode = (record) => {
const { token } = this.props.appInfo;
const tokens = encrypt(token);
const uri = record.uri;
const URL = `${config.apiContextPath}/resource/${tokens}/${encodeURIComponent(uri)}`;
// console.log(document.location.href);
const browerUrl = (document.location.href).split(`${config.contextPath}/main`)[0];
const completeUrl = browerUrl + URL;
this.setState({ completeUrl });
// console.log(completeUrl);
QRcode.toDataURL(completeUrl, (err, url) => {
this.setState({ urls: url, visible: true });
});
// const w = window.open('about:blank');
// w.location.href = URL;
};
onClick = () => {
const { value } = this.props.location.state;
thisPush(this, { pathname: '../addDeploy', state: { value } });
};
handleOk = () => {
this.setState({
visible: false,
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
render() {
const { allAppInfo } = this.props.appInfo;
const { urls, visible, completeUrl } = this.state;
let data = [];
if (allAppInfo.length !== 0) {
allAppInfo.map(({ history }) => {
data = history.map(({ id, updateTime, status, versionNumber, description, uri }) => {
const date = (new Date(updateTime)).toLocaleString();
return {
key: updateTime,
id,
updateTime: date,
status,
versionNumber,
description,
uri,
};
});
return data;
});
}
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.divBtn}>
<Button type="primary" icon="plus" onClick={this.onClick}>新增</Button>
</div>
<div className={styles.divTable}>
<Table columns={Deploy.makeColumns()} dataSource={data} />
</div>
<div>
<Modal
title="二维码"
visible={visible}
onOk={this.handleOk}
onCancel={this.handleCancel}
footer={null}
>
<div style={{ textAlign: 'center' }}>
<img src={urls} alt="二维码" />
<div>
<a href={completeUrl} className={styles.a}>{completeUrl}</a>
</div>
</div>
</Modal>
</div>
</div>
</div>
);
}
}
export default Deploy;
import React from 'react';
import { Button, Input, Form } from 'antd';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const FormItem = Form.Item;
class Edit extends React.Component {
onCancel = () => {
thisPush(this, { pathname: '../list' });
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.dispatch({ type: 'appInfo/editAppInfo', payload: { values } });
}
});
}
render() {
const { value } = this.props.location.state;
const { getFieldDecorator } = this.props.form;
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Form onSubmit={this.handleSubmit}>
<FormItem
label="名称"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('name', {
rules: [{ required: true, message: 'Please input your name!' }],
initialValue: value.name,
})(
<Input disabled />,
)}
</FormItem>
<FormItem
label="新名称"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('newName', {
rules: [{ required: true, message: 'Please input your newName!' }],
})(
<Input />,
)}
</FormItem>
<FormItem
label="包名"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('packageName', {
rules: [{ required: true, message: 'Please input your packageName!' }],
initialValue: value.packageName,
})(
<Input />,
)}
</FormItem>
<FormItem
label="描述"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('description', {
initialValue: value.description,
})(
<Input />,
)}
</FormItem>
<FormItem
wrapperCol={{ span: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
提交
</Button>
<Button style={{ marginLeft: 60 }} onClick={this.onCancel}>
返回
</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
export default Form.create()(Edit);
/**
* Created by zhouhuan on 2017/11/24.
*/
import React from 'react';
import { Button, Input, Select, Form } from 'antd';
import styles from './add.less';
import { thisPush } from '../../../../services/route';
const FormItem = Form.Item;
const { TextArea } = Input;
const Option = Select.Option;
class EditDeploy extends React.Component {
componentDidMount() {
// this.props.dispatch({ type: 'appInfo/getTokens' });
}
onCancel = () => {
const { value } = this.props.location.state;
thisPush(this, { pathname: '../deploy', state: { value } });
};
handleSubmit = (e) => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
const { value } = this.props.location.state;
this.props.dispatch({ type: 'appInfo/editSaveDeploy', payload: { values, name: value.name } });
thisPush(this, { pathname: '../deploy', state: { value } });
}
});
};
render() {
const { getFieldDecorator } = this.props.form;
const { record } = this.props.location.state;
const selects = (
<Select>
<Option key="release">公开版本</Option>
<Option key="development">测试版</Option>
<Option key="broken">重大bug版本</Option>
</Select>
);
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<Form onSubmit={this.handleSubmit}>
<FormItem
label="ID"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('id', {
rules: [{ required: true, message: 'Please input your name!' }],
initialValue: record.id,
})(
<Input disabled />,
)}
</FormItem>
<FormItem
label="部署描述"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('description', {
initialValue: record.description,
})(
<TextArea rows={5} />,
)}
</FormItem>
<FormItem
label="部署状态"
labelCol={{ span: 4 }}
wrapperCol={{ span: 8 }}
>
{getFieldDecorator('status', {
initialValue: record.status,
})(
selects,
)}
</FormItem>
<FormItem
wrapperCol={{ span: 8, offset: 4 }}
>
<Button type="primary" htmlType="submit">
提交
</Button>
<Button style={{ marginLeft: 60 }} onClick={this.onCancel}>
返回
</Button>
</FormItem>
</Form>
</div>
</div>
);
}
}
export default Form.create()(EditDeploy);
/**
* Created by zhouhuan on 2017/11/20.
*/
import List from './list';
import Add from './add';
import Edit from './edit';
import Deploy from './deploy';
import AddDeploy from './addDeploy';
import EditDeploy from './editDeploy';
import route from '../../../../components/hoc/routes';
import model from '../../../../models/main/modules/appManagement';
// noinspection JSUnusedGlobalSymbols
export default (binder) => {
const connect = binder(model);
return route({
childRoutes: [
{
path: 'list',
name: '列表',
component: connect(({ appInfo, loading }) => ({ appInfo, loading }))(List),
},
{
path: 'add',
name: '新增',
component: connect(({ appInfo, loading }) => ({ appInfo, loading }))(Add),
},
{
path: 'edit',
name: '新增',
component: connect(({ appInfo, loading }) => ({ appInfo, loading }))(Edit),
},
{
path: 'deploy',
name: '部署',
component: connect(({ appInfo, loading }) => ({ appInfo, loading }))(Deploy),
},
{
path: 'addDeploy',
name: '新增部署信息',
component: connect(({ appInfo, loading }) => ({ appInfo, loading }))(AddDeploy),
},
{
path: 'editDeploy',
name: '编辑部署信息',
component: connect(({ appInfo, loading }) => ({ appInfo, loading }))(EditDeploy),
},
],
});
};
import React from 'react';
import { Button, Table, Popconfirm } from 'antd';
import styles from './list.less';
import { thisPush } from '../../../../services/route';
class List extends React.Component {
static makeColumns() {
return [{
title: '创建时间',
dataIndex: 'createTime',
}, {
title: '名称',
dataIndex: 'name',
}, {
title: '包名',
dataIndex: 'packageName',
}, {
title: '描述',
dataIndex: 'description',
}, {
title: '操作',
dataIndex: 'operation',
render: (text, record, index) => (
<span>
<span className="ant-divider" />
<a onClick={() => this.onEdit(record, index)}>Edit</a>
<span className="ant-divider" />
<Popconfirm title="确定删除?" okText="Yes" cancelText="No" onConfirm={() => this.onDelete(record, index)}>
<a>Delete</a>
</Popconfirm>
<span className="ant-divider" />
<a onClick={() => this.onDeploy(record, index)}>deploy</a>
</span>
),
}];
}
componentDidMount() {
this.props.dispatch({ type: 'appInfo/getAppInfo', payload: { name: '' } });
}
onDelete = (record) => {
const name = record.name;
this.props.dispatch({ type: 'appInfo/delAppInfo', payload: { name } });
};
onEdit = (record) => {
thisPush(this, { pathname: '../edit', state: { value: record } });
};
onDeploy = (record) => {
thisPush(this, { pathname: '../deploy', state: { value: record } });
this.props.dispatch({ type: 'appInfo/saveRecord', payload: { record } });
};
onClick = () => {
thisPush(this, { pathname: '../add', state: { value: '' } });
};
render() {
const { allAppInfo } = this.props.appInfo;
const data = allAppInfo.map(({ createTime, name, packageName, description }) => {
const date = (new Date(createTime)).toLocaleString();
return {
key: createTime,
createTime: date,
name,
packageName,
description,
};
});
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.divBtn}>
<Button type="primary" icon="plus" onClick={this.onClick}>添加</Button>
</div>
<div className={styles.divTable}>
<Table columns={List.makeColumns()} dataSource={data} />
</div>
</div>
</div>
);
}
}
export default List;
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 18px;
}
.container {
margin-top: 10px;
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
.divBtn{
float: left;
}
.divSelect{
float: right;
}
.divTable{
margin-top: 60px;
border: 1px solid;
border-color:#C4C4C4;
padding: 20px;
border-radius: 10px;
}
.a {
word-wrap:break-word;
}
import React from 'react';
class Detail extends React.Component {
render() {
const { state } = this.props.location;
console.log(state); // eslint-disable-line no-console
return (
<div>List</div>
);
}
}
export default Detail;
import List from './list';
import Detail from './detail';
import route from '../../../../components/hoc/routes';
import model from '../../../../models/main/modules/task';
export default binder => route({
childRoutes: [
{
path: 'list',
name: '列表',
component: binder(model)(({ task }) => ({ task }))(List),
},
{
path: 'detail',
name: '详细',
component: binder(model)(({ task }) => ({ task }))(Detail),
},
],
});
import React from 'react';
import { Button } from 'antd';
import DsTable from '../../../../components/table/dstable';
import { push } from '../../../../services/route';
class List extends React.Component {
render() {
return (
<div>
<Button onClick={() => {
push('../detail');
}}
>
Detail
</Button>
<DsTable coordinate={this.props.task.coordinate} />
</div>
);
}
}
export default List;
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 12px;
}
.container {
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
const Monk = ({ children }) => {
return children || null;
};
export default Monk;
/** @module app */
import isNumber from 'lodash/isNumber';
import isFinite from 'lodash/isFinite';
import isString from 'lodash/isString';
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
/**
* @typedef {Object} RestResponse
* @template T
* @property {number} errorCode 错误码,0表示成功
* @property {T} data 数据
*/
/**
* @typedef {Object} DeploymentInfo 部署信息
* @property {number} id 部署id
* @property {string} versionNumber 版本号
* @property {string} description 描述,一般为更新说明
* @property {Date} updateTime 部署发布时间
* @property {string} uri 资源内部uri,表示app安装包文件的内部定位地址
* @property {string} status 部署状态,可能值为release,development,broken。release表示公开版本,development表示内部测试版,broken表示有重大bug,短时间不能解决,需要回滚
*/
/**
* @typedef {Object} AppInfo app信息
* @property {string} name 名称
* @property {string} description 描述
* @property {string} packageName 包名
* @property {Date} createTime 创建时间
* @property {Array.<DeploymentInfo>} history 历史部署信息
*/
/**
* 根据名称或许符合条件的app相关信息,其中也包括部署信息
* @param {!string} name 对app名字的筛选条件,支持模糊查询,为空时等价于查全部
* @param {?number} pst 分页的开始位置,-1代表不分页
* @param {?number} psz 分页的大小
* @returns {Promise.<RestResponse.<Array.<AppInfo>>>} 所有符合条件的app信息
*/
export const getAppInfoes = async (name, pst = -1, psz = -1) => {
return request(`${config.apiContextPath}/api/app/admin/apps/list`, { name, pst, psz });
};
/**
* 新增app
* @param {!string} name 名称
* @param {?string} packageName
* @param {?string} description
* @returns {Promise.<RestResponse.<AppInfo>>} 新增的app信息
*/
export const addApp = async (name, packageName, description) => {
return post(`${config.apiContextPath}/api/app/admin/apps/create`, { name, package: packageName, description });
};
/**
* 更新app信息,忽略undefined的字段
* @param {!number|string} idOrName app的id或名称,若为数字,则认为是id,字符串则认为是名称
* @param {?string} newName 新的名称
* @param {?string} packageName 包名
* @param {?string} description 描述
* @returns {Promise.<RestResponse.<null>>}
*/
export const editApp = async (idOrName, newName, packageName, description) => {
if (isNumber(idOrName) && !isFinite(idOrName)) {
return post(`${config.apiContextPath}/api/app/admin/apps/edit`, { id: idOrName, newName, package: packageName, description });
} else if (isString(idOrName)) {
return post(`${config.apiContextPath}/api/app/admin/apps/edit`, { name: idOrName, newName, package: packageName, description });
} else {
throw new Error(`Invalid id or name: ${idOrName}`);
}
};
/**
* 删除app,所有部署包会一同删除
* @param {!number|string} idOrName app的id或名称,若为数字,则认为是id,字符串则认为是名称
* @returns {Promise.<RestResponse.<null>>}
*/
export const removeApp = async (idOrName) => {
if (isNumber(idOrName) && !isFinite(idOrName)) {
return post(`${config.apiContextPath}/api/app/admin/apps/delete`, { id: idOrName });
} else if (isString(idOrName)) {
return post(`${config.apiContextPath}/api/app/admin/apps/delete`, { name: idOrName });
} else {
throw new Error(`Invalid id or name: ${idOrName}`);
}
};
/**
* 部署app,必须是一个新版本,若版本号低于或等于已部署的最高版本,则会部署失败
* @param {!number|string} idOrName app的id或名称,若为数字,则认为是id,字符串则认为是名称
* @param {!string} uri apk上传后得到的内部uri
* @param {?string} description 更新说明
* @param {?boolean} release 是否release版本,默认true
* @returns {Promise.<RestResponse.<DeploymentInfo>>} 部署信息
*/
export const deployApp = async (idOrName, uri, description, release) => {
if (isNumber(idOrName) && !isFinite(idOrName)) {
return post(`${config.apiContextPath}/api/app/admin/apps/deploy`, { id: idOrName, uri, description, release });
} else if (isString(idOrName)) {
return post(`${config.apiContextPath}/api/app/admin/apps/deploy`, { name: idOrName, uri, description, release });
} else {
throw new Error(`Invalid id or name: ${idOrName}`);
}
};
/**
* 反部署app
* @param {!number} id 部署app后返回的部署信息中记录的部署id
* @returns {Promise.<RestResponse.<null>>}
*/
export const undeployApp = async (id) => {
return post(`${config.apiContextPath}/api/app/admin/apps/undeploy`, { id });
};
/**
* 修改部署信息
* @param {!number} id 部署app后返回的部署信息中记录的部署id
* @param {?string} description 部署描述,即更新说明
* @param {?string} status 部署状态,可能值为release,development,broken。release表示公开版本,development表示内部测试版,broken表示有重大bug,短时间不能解决,需要回滚
* @returns {Promise.<RestResponse.<null>>}
*/
export const editDeployment = async (id, description, status) => {
return post(`${config.apiContextPath}/api/app/admin/apps/deployments/edit`, { id, description, status });
};
import request from '../utils/request';
import config from '../utils/config';
export async function countTasks(filters = []) {
return request(`${config.apiContextPath}/api/bpm/task/all/count`, filters);
}
export async function fetchTasks({ pst, psz, filters }) {
const queryParams = [...filters];
queryParams.push(['pst', pst]);
queryParams.push(['psz', psz]);
return request(`${config.apiContextPath}/api/bpm/task/all/info`, queryParams);
}
import isString from 'lodash/isString';
import partial from 'lodash/fp/partial';
import request, { makeParams } from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
import { getDomain } from '../utils/auth';
export const datasourceApi = (coordinate) => {
const { containerType, containerName, datasourceName } = isString(coordinate) ? {
containerType: 'global',
datasourceName: coordinate,
} : (coordinate || {});
if (containerType === 'global') {
return {
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]),
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}`);
}
};
export async function calcGlobalDatasource(name, { pst, psz, filters = [], sortBys = [], sortTypes = [], params = {}, dmPath }) {
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/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/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/user/${encodeURIComponent(name)}/update/validate`, makeParams({ key, dmPath }, { params }));
}
export async function updateGlobalDatasource(name, key, params = {}, dmPath) {
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,
});
}
export async function validateCreateGlobalDatasource(name, params = {}, dmPath) {
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/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/user/${encodeURIComponent(name)}/remove/validate`, makeParams({ key, dmPath }, { params }));
}
export async function removeGlobalDatasource(name, key, params = {}, dmPath) {
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, 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/${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/${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/${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/${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/${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,
});
}
export async function validateCreateModuleDatasource(mdName, dsName, params = {}, dmPath) {
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/${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/${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/${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, 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 });
}
}
/* eslint-disable no-param-reassign */
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
import { getDomain, getUser, histories } from '../utils/auth';
export async function fetchDomains(basePath, withRoot = false) {
if (!basePath) {
const domain = await getDomain();
if (!domain) {
throw new Error('Not set domain yet!');
}
basePath = domain.path;
}
const rootDomainInfo = withRoot ? await request(`${config.apiContextPath}/api/domain/user/info`, { dmPath: basePath }) : null;
const childrenDomainInfoList = await request(`${config.apiContextPath}/api/domain/user/children-info`, { dmPath: basePath });
let infoList;
if (rootDomainInfo && childrenDomainInfoList) {
infoList = [
rootDomainInfo,
...childrenDomainInfoList,
];
} else if (rootDomainInfo) {
infoList = [rootDomainInfo];
} else if (childrenDomainInfoList) {
infoList = childrenDomainInfoList;
} else {
infoList = [];
}
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 });
const user = await getUser();
histories.pushHistory('domain', user && user.id, path);
}
export async function currentDomain() {
return request(`${config.apiContextPath}/api/domain/user/info`);
}
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() {
return request(`${config.apiContextPath}/api/interface/user/info`);
},
async getInterfaceInfo(name) {
return request(`${config.apiContextPath}/api/interface/user/${encodeURIComponent(name)}/info`);
},
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 = {
async getAllInterfaceInfoes(dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/info`, { dmPath });
},
async getInterfaceInfo(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/info`, { dmPath });
},
async getInterfaceConfigure(name, dmPath) {
return request(`${config.apiContextPath}/api/interface/admin/${encodeURIComponent(name)}/configure`, { dmPath });
},
async getInterfacePlainConfigure(name, 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-param-reassign */
/** @module services/login */
import { getDeviceId } from '../../utils/device';
import post from '../../utils/post';
import request from '../../utils/request';
import { encrypt } from '../../utils/helper';
import { getToken } from '../../utils/auth';
import config from '../../utils/config';
/**
* @typedef {Object} TokenInfo
* @property {number} [life]
* @property {boolean} [persist]
* @property {!string} productId
* @property {!string} deviceId
*/
/**
* @typedef {Object} LoginRequest
* @property {!string} type
* @property {string} [data]
* @property {TokenInfo} [tokenInfo]
* @property {AuthRequest} [authRequest]
*/
/**
* @typedef {Object} AuthRequest
* @property {!string} type
* @property {Object} [parameters]
*/
/**
* @typedef {Object} LoginResponse
* @property {!string} tokenId
* @property {?AuthResponse} authResponse
* @property {!AuthRequirements} remainedAuthRequirements
*/
/**
* @typedef {Object} AuthResponse
* @property {!string} type
* @property {!string} status
* @property {?Object} data
*/
/**
* @typedef {Object} AuthRequirements
* @property {Array.<AuthRequirement>} requirements
*/
/**
* @typedef {Object} AuthRequirement
* @property {Array.<string>} authTypes
*/
/**
* 登录
* @param {!LoginRequest} loginRequest 登录请求
* @returns {Promise.<LoginResponse>}
*/
export async function login(loginRequest) {
if (!loginRequest.tokenInfo) {
loginRequest.tokenInfo = {
productId: config.productId,
deviceId: `${getDeviceId()}`,
};
}
return post(`${config.apiContextPath}/api/auth/login`, loginRequest, {}, {}, false);
}
/**
* 认证
* @param {{tkId: ?string, request: AuthRequest}} authRequest 认证请求
* @return {Promise.<AuthResponse>}
*/
export async function authorize(authRequest) {
if (!authRequest.tkId) {
authRequest.tkId = encrypt(await getToken());
}
return post(`${config.apiContextPath}/api/auth/authorize`, authRequest, {}, {}, false);
}
export async function userInfo() {
return request(`${config.apiContextPath}/api/user/info`);
}
import { addToken } from './utils';
export const validate = async (password, token) => {
const request = {
type: 'password',
parameters: {
cipher: password,
},
};
return addToken(request, token);
};
import { addToken } from './utils';
export const requestCode = async (token) => {
const request = {
type: 'uca',
parameters: {
action: 'request',
},
};
return addToken(request, token);
};
export const validate = async (signed, token) => {
const request = {
type: 'uca',
parameters: {
action: 'validate',
response: signed,
},
};
return addToken(request, token);
};
import { getToken } from '../../utils/auth';
export const addToken = async (request, token) => {
if (token) {
return {
tkId: token,
request,
};
} else {
let tkId;
const localToken = await getToken();
if (localToken) {
// eslint-disable-next-line no-param-reassign
tkId = localToken;
}
if (tkId) {
return {
tkId,
request,
};
} else {
return {
request,
};
}
}
};
import post from '../utils/post';
import config from '../utils/config';
export async function logout() {
return post(`${config.apiContextPath}/api/auth/logout`);
}
/* 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`);
}
export async function fetchModuleInfos() {
return request(`${config.apiContextPath}/api/module/user/info`);
}
export async function fetchModuleLayout(name) {
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 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() {
this.operations = [];
}
addCmd = (cmd, ...args) => {
this.operations.push({ cmd, args });
return this;
};
setName = (name) => {
return this.addCmd('setName', name);
};
setRight = (right) => {
return this.addCmd('setRight', right);
};
addTags = (...tags) => {
return this.addCmd('addTags', ...tags);
};
removeTags = (...tags) => {
return this.addCmd('removeTags', ...tags);
};
clearTags = () => {
return this.addCmd('clearTags');
};
setTags = (...tags) => {
return this.addCmd('setTags', ...tags);
};
addOwners = (...owners) => {
return this.addCmd('addOwners', ...owners);
};
removeOwners = (...owners) => {
return this.addCmd('removeOwners', ...owners);
};
clearOwners = () => {
return this.addCmd('clearOwners');
};
setOwners = (...owners) => {
return this.addCmd('setOwners', ...owners);
};
addOwnerGroups = (...groups) => {
return this.addCmd('addOwnerGroups', ...groups);
};
removeOwnerGroups = (...groups) => {
return this.addCmd('removeOwnerGroups', ...groups);
};
clearOwnerGroups = () => {
return this.addCmd('clearOwnerGroups');
};
setOwnerGroups = (...groups) => {
return this.addCmd('setOwnerGroups', ...groups);
};
use = (usage) => {
return this.addCmd('use', usage);
};
unuse = (usage) => {
return this.addCmd('unuse', usage);
};
}
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();
};
export async function editResource(uri, operations) {
return post(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`, { operations: operations.operations });
}
export async function deleteResource(uri) {
return doDelete(await rsLink(uri));
}
export async function countResource(query) {
return post(`${config.apiContextPath}/api/resource/user/count`, query);
}
export async function queryResource(query) {
return post(`${config.apiContextPath}/api/resource/user/query`, query);
}
export async function getResourceInfo(uri) {
return request(`${config.apiContextPath}/api/resource/user/${encodeURIComponent(uri)}/meta`);
}
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 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);
}
return {
...path,
pathname: makePath(base, path.pathname, withContext),
};
};
const getHistoryBase = (history) => {
if (history.location) {
return history.location.pathname;
} else {
return location.pathname;
}
};
let history;
let location;
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) => {
return getStore().dispatch(pushAction(processPath(getHistoryBase(history), path, false), state));
};
export const replace = (path, state) => {
return getStore().dispatch(replaceAction(processPath(getHistoryBase(history), path, false), state));
};
export const go = (n) => {
return getStore().dispatch(goAction(n));
};
export const goBack = () => {
return getStore().dispatch(goBackAction());
};
export const goForward = () => {
return getStore().dispatch(goForwardAction());
};
export const thisPush = (theThis, pathOrLoc, state) => {
return push(pathOrLoc, state);
};
export const thisReplace = (theThis, pathOrLoc, state) => {
return replace(pathOrLoc, state);
};
export const thisGo = (theThis, n) => {
return go(n);
};
export const thisGoBack = () => {
return goBack();
};
export const thisGoForward = () => {
return goForward();
};
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';
import config from '../utils/config';
// noinspection JSUnusedGlobalSymbols
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]),
},
};
};
export async function getAllAdminTemplateInfoes(dmPath) {
return request(`${config.apiContextPath}/api/template/admin/info`, { dmPath });
}
export async function getAdminTemplateInfo(name, dmPath) {
return request(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/info`, { dmPath });
}
export async function getAdminTemplateConfigure(name, dmPath) {
return request(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/configure`, { dmPath });
}
export async function getAdminTemplatePlainConfigure(name, dmPath) {
return request(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/plainConfigure`, { dmPath });
}
export async function setAdminTemplateConfigure(name, configure, dmPath) {
return post(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/configure`, { configure, dmPath });
}
export async function removeAdminTemplateConfigure(name, dmPath) {
return doDelete(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/configure`, { dmPath });
}
export async function setAdminTemplateTemplate(name, uri, dmPath) {
return post(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/template`, { uri, dmPath });
}
export async function getAdminTemplateTemplate(name, dmPath) {
return request(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/template`, { dmPath });
}
export async function adminRender(name, params, mediaType, dmPath) {
return post(`${config.apiContextPath}/api/template/admin/${encodeURIComponent(name)}/render`, { dmPath, mediaType, params });
}
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 post from '../utils/post';
import config from '../utils/config';
export const bindCert = async (uid, cert) => {
return post(`${config.apiContextPath}/api/uca/admin/cert`, { uid, cert });
};
import partial from 'lodash/partial';
import request from '../utils/request';
import post from '../utils/post';
import config from '../utils/config';
export const userApi = (id) => {
return {
admin: {
getConfigure: partial(getUserConfigure, id),
setConfigure: partial(setUserConfigure, id),
refresh: partial(refreshUser, id),
},
};
};
export const roleApi = (id) => {
return {
admin: {
getConfigure: partial(getRoleConfigure, id),
setConfigure: partial(setRoleConfigure, id),
refresh: partial(refreshRole, id),
},
};
};
export async function getUserConfigure(id) {
return request(`${config.apiContextPath}/api/user/admin/user/${id}/configure`);
}
export async function setUserConfigure(id, configure) {
return post(`${config.apiContextPath}/api/user/admin/user/${id}/configure`, { configure });
}
export async function refreshUser(id) {
return post(`${config.apiContextPath}/api/user/admin/user/${id}/refresh`);
}
export async function getRoleConfigure(id) {
return request(`${config.apiContextPath}/api/user/admin/role/${id}/configure`);
}
export async function setRoleConfigure(id, configure) {
return post(`${config.apiContextPath}/api/user/admin/role/${id}/configure`, { configure });
}
export async function refreshRole(id) {
return post(`${config.apiContextPath}/api/user/admin/role/${id}/refresh`);
}
import { makeAsync as _makeAsync } from 'react-async-wrapper';
import { processError } from './error';
export const makeAsync = (opt, ...args) => _makeAsync({
onError: processError,
...opt,
}, ...args);
/* eslint-disable no-param-reassign */
/**
* Created by yaohx_169 on 2017/6/8.
*/
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 getProductValue(cookie.token);
}
export async function setToken(token) {
setProductValue(cookie.token, token);
}
export async function delToken() {
delProductValue(cookie.token);
}
export async function getUser() {
const id = getProductValue(cookie.userId);
return id ? {
id,
name: getProductValue(cookie.userName),
} : null;
}
export async function setUser(id, name) {
setProductValue(cookie.userId, id);
setProductValue(cookie.userName, name);
}
export async function delUser() {
delProductValue(cookie.userId);
delProductValue(cookie.userName);
}
export async function getDomain() {
const path = getUserValue(cookie.domainPath);
return path ? {
name: getUserValue(cookie.domainName),
path,
} : null;
}
export async function setDomain(name, path) {
setUserValue(cookie.domainName, name);
setUserValue(cookie.domainPath, path);
}
export async function delDomain() {
delUserValue(cookie.domainName);
delUserValue(cookie.domainPath);
}
export async function isAuthed() {
return getToken().then(result => !!result);
}
export async function hasDomain() {
return getDomain().then(result => !!result);
}
const normHistory = size => (history) => {
if (!history) {
history = {};
}
if (!history.size) {
if (size) {
history.size = size;
} else {
history.size = 10;
}
} else if (size && history.size !== size) {
history.size = size;
}
if (history.size < 1) {
history.size = 1;
}
if (!history.data) {
history.data = [];
history.start = 0;
history.top = 0;
history.empty = true;
}
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}`);
}
if (i + 1 >= size) {
return 0;
} else {
return i + 1;
}
};
const prev = (i, size) => {
if (i < 0 || i >= size) {
throw new Error(`out of range: ${i} in ${size}`);
}
if (i - i < 0) {
return size - 1;
} else {
return i - 1;
}
};
export const histories = {
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 target.data[prev(target.top, history.size)];
});
},
async createHistory(name, uid, size) {
return db([config.productId, 'history', name]).write(normHistory(size), normData(uid));
},
async destroyHistory(name, uid) {
const path = [config.productId, 'history', name];
if (uid) {
path.push(uid);
}
return db(path).delete();
},
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 (target.top > target.start) {
return target.data.slice(target.start, target.top);
} else {
return [...target.data.slice(target.start, h.size), ...target.data.slice(0, target.top)];
}
});
},
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 {
target.top = nextPos;
}
if (target.empty) {
target.empty = false;
}
return history;
});
},
async popHistory(name, uid) {
return db([config.productId, 'history', name]).write(normHistory(), normData(uid), (history) => {
const target = selectData(uid)(history);
if (target.empty) {
return;
}
target.top = prev(target.top, history.size);
if (target.top === target.start) {
target.empty = true;
}
return history;
});
},
async init() {
return db.read();
},
};
import baseConfig from '../../config';
/* eslint-disable no-undef */
/**
* Created by yaohx_169 on 2017/6/6.
*/
export const cookie = {
token: 'token',
userId: 'userId',
userName: 'userName',
domainPath: 'domainPath',
domainName: 'domainName',
};
export const errors = {
exception: 0x00010000,
no_such_user: 0x00010001,
wrong_password: 0x00010002,
invalid_token: 0x00010003,
invalid_query_param: 0x00010004,
domain_not_set: 0x00010005,
wrong_sms_code: 0x00010006,
sms_code_time_out: 0x00010007,
invalid_mobile: 0x00010008,
no_operation_right: 0x00010100,
no_module_right: 0x00010101,
no_interface_right: 0x00010102,
no_batch_right: 0x00010103,
no_resource_right: 0x00010104,
no_domain_right: 0x00010105,
no_datasource_right: 0x00010106,
general_error: 0x00011111,
// client error:
token_missing: 0x00000001,
unsupported_auth_type: 0x00000003,
auth_failed: 0x00000004,
};
export const api = {
userLogin: '/user/login',
userLogout: '/user/logout',
userInfo: '/userInfo',
users: '/users',
user: '/user/:id',
dashboard: '/dashboard',
};
const defaultDateFormat = 'YYYY-MM-DD';
const defaultTimeFormat = 'HH:mm:ss';
const defaultDateTimeFormat = `${defaultDateFormat} ${defaultTimeFormat}`;
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`,
basename,
contextPath,
apiContextPath,
productId: 'app-manage-console',
fastNavigationPage: '',
defaultDateFormat,
defaultTimeFormat,
defaultDateTimeFormat,
pubKey: '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+/Rs6dYmdtETjHCZq4LF3QjLM/DocRAXrqtMULZf+579dAn+CiM8noLplZT/DRwvfK822eq8sypH+a4NqP7942pPVjOudVvKfiJvmm2TOQHvQ7vi3iyZVdlsxX72JNFo1Ocqwj48aIC/OJ4bMf/VyCKrmKrU2iXND+I4BN8cfhwIDAQAB-----END PUBLIC KEY-----',
};
export default config;
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 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';
// eslint-disable-next-line no-underscore-dangle
const _adapter_ = new LocalStorage('db');
/**
* @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();
};
/**
* @typedef {Function} CreateDB
* @param {string} path
* @param {*} [defaultValue]
* @return {DB}
*/
/**
* @member {Function} CreateDB~read
* @return {*|Promise.<*>}
*/
/**
* @member {Function} CreateDB~write
* @param {*} [state]
* @return {*|Promise.<*>}
*/
/**
* @member {Function} CreateDB~plant
* @param {*} state
* @return {CreateDB}
*/
/**
* @member {Function} CreateDB~getState
* @return {*}
*/
/**
* @member {Function} CreateDB~setState
* @param {*} state
* @return {CreateDB}
*/
/**
* @typedef {Function} DB
* @param {...Function} functions
* @return {*}
*/
/**
* @member {Function} DB~write
* @param {...Function} functions
* @return {*|Promise.<*>}
*/
/**
* @member {Function} DB~delete
* @return {*|Promise.<*>}
*/
/**
* @return {CreateDB}
*/
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;
}
return init(db, '__state__', _adapter_);
};
export default CreateDB();
/* eslint-disable no-param-reassign */
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';
const defaultOptions = {
headers: { Accept: 'application/json' },
};
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to delete
* @param auth
* @param params
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default async function doDelete(url, params = {}, options = {}, auth = true) {
params = normParams(params);
const res = await middleware.delete.onRequest(url, params, options, auth);
let queryParams = (res ? res.params : params).map(([k, v]) => (isNil(v) ? k : `${k}=${encodeURIComponent(v)}`));
queryParams = queryParams.join('&');
let realUrl = res ? res.url : url;
if (queryParams) {
realUrl = `${url}?${queryParams}`;
}
const realOptions = defaults(res ? res.options : options, defaultOptions);
realOptions.method = 'DELETE';
return fetch(realUrl, realOptions)
.then(checkStatus)
.then(resp => parseObject(resp, middleware.delete.onResponse));
}
import Fingerprint from 'fingerprintjs';
export function getDeviceId() {
// noinspection JSUnresolvedFunction
return new Fingerprint({ ie_activex: true }).get();
}
/* eslint-disable no-param-reassign,prefer-rest-params */
/**
* Created by yaohx_169 on 2017/5/10.
*/
/*
* 添加事件处理程序
* @param object object 要添加事件处理程序的元素
* @param string type 事件名称,如click
* @param function handler 事件处理程序,可以直接以匿名函数的形式给定,或者给一个已经定义的函数名。匿名函数方式给定的事件处理程序在IE6 IE7 IE8中可以移除,在标准浏览器中无法移除。
* @param boolean remove 是否是移除的事件,本参数是为简化下面的removeEvent函数而写的,对添加事件处理程序不起任何作用
*/
export function addEvent(object, type, handler, remove) {
if (typeof object !== 'object' || typeof handler !== 'function') return;
try {
object[remove ? 'removeEventListener' : 'addEventListener'](type, handler, false);
} catch (e) {
const xc = `_${type}`;
object[xc] = object[xc] || [];
if (remove) {
const l = object[xc].length;
for (let i = 0; i < l; i++) {
if (object[xc][i].toString() === handler.toString()) object[xc].splice(i, 1);
}
} else {
const l = object[xc].length;
let exists = false;
for (let i = 0; i < l; i++) {
if (object[xc][i].toString() === handler.toString()) exists = true;
}
if (!exists) object[xc].push(handler);
}
object[`on${type}`] = function cb() {
const l = object[xc].length;
for (let i = 0; i < l; i++) {
object[xc][i].apply(object, arguments);
}
};
}
}
/*
* 移除事件处理程序
*/
export function removeEvent(object, type, handler) {
addEvent(object, type, handler, true);
}
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';
const errStyle = {
overflow: 'auto',
wordWrap: 'break-word',
width: '360px',
height: '360px',
marginLeft: '-24px',
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:
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);
}
} else if (err instanceof Error) {
showError(err);
}
}
}
function showError(err) {
if (err) {
if (err.data && err.data.errorCode) {
let msg;
if (err.data) {
const data = err.data;
msg = data ? data.message : err.message;
} else {
msg = err.message;
}
if (msg && msg.length < 256) {
msgError(msg);
} else {
Modal.error({
title: '服务器内部错误',
content: <div style={errStyle}>{msg}</div>,
width: 460,
});
}
} else if (err instanceof Error) {
Modal.error({
title: '错误!',
content: (
<div style={errStyle}>
<p>{err.message ? err.message : ''}</p>
<p>{err.stack ? err.stack : ''}</p>
</div>
),
width: 460,
});
}
}
}
export function createError({ code, msg }) {
const error = new Error(msg);
error.data = {
errorCode: code,
message: msg,
};
return error;
}
export const errors = {
tokenMissing: () => createError({
code: errorCodes.token_missing,
msg: '需要登录!',
}),
wrongPassword: () => createError({
code: errorCodes.wrong_password,
msg: '密码错误!',
}),
authFailed: msg => createError({
code: errorCodes.auth_failed,
msg: `登录验证失败!${msg || ''}`,
}),
unsupportedAuthType: (...types) => createError({
code: errorCodes.unsupported_auth_type,
msg: `不支持的客户端验证方式:${types.join('、')}.`,
}),
generalError: msg => createError({
code: errorCodes.general_error,
msg: `${msg || '未知错误。'}`,
}),
};
const findSep = (value, offset = 0) => {
const find = value.indexOf('|', offset);
if (find !== -1) {
let count = 0;
let from = find;
while (value[--from] === '#') ++count;
if ((count & 1) === 1) {
return findSep(value, find + 1);
} else {
return find;
}
} else {
return -1;
}
};
const transform = (value) => {
const find = value.indexOf('#');
if (find !== -1) {
const left = value.slice(0, find);
const me = value[find + 1] ? value[find + 1] : '';
const right = value.slice(find + 2);
return `${left}${me}${transform(right)}`;
} else {
return value;
}
};
export const split = (value, unescape = true) => {
const ret = [];
let offset = 0;
let posSep = -1;
do {
posSep = findSep(value, offset);
if (posSep !== -1) {
if (unescape) {
ret.push(transform(value.slice(offset, posSep)));
} else {
ret.push(value.slice(offset, posSep));
}
offset = posSep + 1;
}
} while (posSep !== -1);
if (unescape) {
ret.push(transform(value.slice(offset)));
} else {
ret.push(value.slice(offset));
}
return ret;
};
/* eslint-disable no-param-reassign */
import moment from 'moment';
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};` : ' path=/;';
const valueSet = value ? `${name}=${encodeURIComponent(value)};` : '';
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) {
let value = arr.map(v => v.substring(`${name}=`.length).trim()).filter(v => !!v).pop();
if (value.endsWith(';')) {
value = value.substring(0, value.length - 1);
}
return decodeURIComponent(value);
} else {
return null;
}
}
export function delCookie(name, { domain, path } = {}) {
if (getCookie(name)) {
name = toKey(name);
const domainSet = domain ? ` domain=${domain};` : '';
const pathSet = path ? ` path=${path};` : ' path=/';
document.cookie = `${name}=; expires=Thu, 01-Jan-70 00:00:01 GMT;${pathSet}${domainSet}`; // eslint-disable-line
}
}
export function setLocalStorge(key, value) {
return localStorage.setItem(key, JSON.stringify(value)); // eslint-disable-line
}
export function getLocalStorge(key) {
return JSON.parse(localStorage.getItem(key)); // eslint-disable-line
}
export function delLocalStorge(key) {
return localStorage.removeItem(key); // eslint-disable-line
}
export function locationOrigin(withContext = true) {
return `${location.protocol}//${location.hostname}${location.port ? ':' + location.port : ''}${withContext ? contextPath : ''}`; // eslint-disable-line
}
export const makeSureEndsWithSlash = (path) => {
if (!path || !path.endsWith('/')) {
return `${path || ''}/`;
} else {
return 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 makeSureStartsWithSlash(path.slice(contextPath.length));
} else {
return path;
}
}
export function encrypt(text) {
const jsEncrypt = createJSEncrypt();
jsEncrypt.setPublicKey(pubKey);
let out = jsEncrypt.encryptLong(text);
out = out.split('=')[0];
out = out.replace(/\+/g, '-');
return out.replace(/\//g, '_');
}
/**
* @param name {String}
* @return {String}
*/
export function queryURL(name) {
const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
// 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;
}
export function padDigits(number, digits) {
return new Array(Math.max(digits - String(number).length + 1, 0)).join('0') + number;
}
export function is(obj, 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) {
return new Promise((resolve) => {
thunk(data => resolve(data));
});
}
export function makePromise1(thunk) {
return new Promise((resolve, reject) => {
thunk(err => reject(err), data => resolve(data));
});
}
export function filterValidParams(params) {
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 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) {
if (response.status >= 200 && response.status < 300) {
return response;
}
return response.json().then((data) => {
const error = new Error(data.message ? data.message : response.statusText);
error.data = data;
throw error;
});
}
export function normParams(unnormed) {
if (isPlainObject(unnormed)) {
return flow(
toPairs,
flatMap(([k, v]) => (isArray(v) ? v.map(vv => [k, vv]) : [[k, v]])),
)(unnormed);
} else {
return unnormed;
}
}
// eslint-disable-next-line max-len
export function parseObject(response, middleware, { num2str = false, bool2str = false, nul2str = false, ud2str = false, nil2str = false } = {}) {
if (response.status === 204) { // no-content
return null;
} else {
const contentType = response.headers.get('content-type');
if (contentType) {
const needMap = num2str || bool2str || nul2str || ud2str || nil2str;
const mapStr = (value) => {
if (num2str && isNumber(value)) {
return value.toString();
}
if (bool2str && isBoolean(value)) {
return value.toString();
}
if (nul2str && isNull(value)) {
return '';
}
if (ud2str && isUndefined(value)) {
return '';
}
if (nil2str && isNil(value)) {
return '';
}
return value;
};
const mapObj = (obj, mapArrFunc) => {
if (isArray(obj)) {
return mapArrFunc(obj, mapObj);
}
if (isPlainObject(obj)) {
return mapValues(obj, (val) => {
const ret = mapStr(val);
return mapObj(ret, mapArrFunc);
});
}
return obj;
};
const mapArr = (arr, mapObjFunc) => {
if (isPlainObject(arr)) {
return mapObjFunc(arr, mapArr);
}
if (isArray(arr)) {
return map(arr, (val) => {
const ret = mapStr(val);
return mapArr(ret, mapObjFunc);
});
}
return arr;
};
const mapValue = curry(mapObj)(_, mapArr);
if (contentType.indexOf('json') !== -1) {
return response.json()
.then((json) => {
let out = json;
if (isObjectLike(out)) {
out = new Resolver(out).resolve();
}
return middleware ? middleware(out) : out;
})
.then((data) => {
return needMap ? mapValue(data) : data;
});
} else if (contentType.indexOf('xml') !== -1) {
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)) {
out = new Resolver(out).resolve();
}
return middleware ? middleware(out) : out;
})
.then((data) => {
return needMap ? mapValue(data) : data;
});
} else if (contentType.indexOf('text') !== -1) {
return response.text();
} else {
throw new Error(`Unsupported response content type: ${contentType}`);
}
} else {
throw new Error('No response content type.');
}
}
}
/* eslint-disable */
var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64padchar="=";
function hex2b64(h) {
var i;
var c;
var ret = "";
for(i = 0; i+3 <= h.length; i+=3) {
c = parseInt(h.substring(i,i+3),16);
ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
}
if(i+1 == h.length) {
c = parseInt(h.substring(i,i+1),16);
ret += b64map.charAt(c << 2);
}
else if(i+2 == h.length) {
c = parseInt(h.substring(i,i+2),16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
}
while((ret.length & 3) > 0) ret += b64padchar;
return ret;
}
// convert a base64 string to hex
function b64tohex(s) {
var ret = ""
var i;
var k = 0; // b64 state, 0-3
var slop;
for(i = 0; i < s.length; ++i) {
if(s.charAt(i) == b64padchar) break;
v = b64map.indexOf(s.charAt(i));
if(v < 0) continue;
if(k == 0) {
ret += int2char(v >> 2);
slop = v & 3;
k = 1;
}
else if(k == 1) {
ret += int2char((slop << 2) | (v >> 4));
slop = v & 0xf;
k = 2;
}
else if(k == 2) {
ret += int2char(slop);
ret += int2char(v >> 2);
slop = v & 3;
k = 3;
}
else {
ret += int2char((slop << 2) | (v >> 4));
ret += int2char(v & 0xf);
k = 0;
}
}
if(k == 1)
ret += int2char(slop << 2);
return ret;
}
// convert a base64 string to a byte/number array
function b64toBA(s) {
//piggyback on b64tohex for now, optimize later
var h = b64tohex(s);
var i;
var a = new Array();
for(i = 0; 2*i < h.length; ++i) {
a[i] = parseInt(h.substring(2*i,2*i+2),16);
}
return a;
}
/* eslint-disable */
// Copyright (c) 2005 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.
// Basic JavaScript BN library - subset useful for RSA encryption.
// Bits per digit
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.
// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}
// (protected) copy this to r
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}
// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+this.DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
d |= this[--i]>>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}
// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }
// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return (this.s<0)?-r:r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}
// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}
// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}
// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;
// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;
// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);
/* eslint-disable no-unused-vars,camelcase */
// prng4.js - uses Arcfour as a PRNG
function Arcfour() {
this.i = 0;
this.j = 0;
this.S = [];
}
// Initialize arcfour context from key, an array of ints, each from [0..255]
function ARC4init(key) {
let i;
let j;
let t;
for (i = 0; i < 256; ++i) { this.S[i] = i; }
j = 0;
for (i = 0; i < 256; ++i) {
j = (j + this.S[i] + key[i % key.length]) & 255;
t = this.S[i];
this.S[i] = this.S[j];
this.S[j] = t;
}
this.i = 0;
this.j = 0;
}
function ARC4next() {
this.i = (this.i + 1) & 255;
this.j = (this.j + this.S[this.i]) & 255;
const t = this.S[this.i];
this.S[this.i] = this.S[this.j];
this.S[this.j] = t;
return this.S[(t + this.S[this.i]) & 255];
}
Arcfour.prototype.init = ARC4init;
Arcfour.prototype.next = ARC4next;
// Plug in your RNG constructor here
function prng_newstate() {
return new Arcfour();
}
// Pool size must be a multiple of 4 and greater than 32.
// An array of bytes the size of the pool will be passed to init()
const rng_psize = 256;
/* eslint-disable */
// Random number generator - requires a PRNG backend, e.g. prng4.js
// For best results, put code like
// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
// in your main HTML document.
import './prng4';
var rng_state;
var rng_pool;
var rng_pptr;
// Mix in a 32-bit integer into the pool
function rng_seed_int(x) {
rng_pool[rng_pptr++] ^= x & 255;
rng_pool[rng_pptr++] ^= (x >> 8) & 255;
rng_pool[rng_pptr++] ^= (x >> 16) & 255;
rng_pool[rng_pptr++] ^= (x >> 24) & 255;
if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
}
// Mix in the current time (w/milliseconds) into the pool
function rng_seed_time() {
rng_seed_int(new Date().getTime());
}
// Initialize the pool with junk if needed.
if(rng_pool == null) {
rng_pool = new Array();
rng_pptr = 0;
var t;
if(window.crypto && window.crypto.getRandomValues) {
// Use webcrypto if available
var ua = new Uint8Array(32);
window.crypto.getRandomValues(ua);
for(t = 0; t < 32; ++t)
rng_pool[rng_pptr++] = ua[t];
}
if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
// Extract entropy (256 bits) from NS4 RNG if available
var z = window.crypto.random(32);
for(t = 0; t < z.length; ++t)
rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
}
while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
t = Math.floor(65536 * Math.random());
rng_pool[rng_pptr++] = t >>> 8;
rng_pool[rng_pptr++] = t & 255;
}
rng_pptr = 0;
rng_seed_time();
//rng_seed_int(window.screenX);
//rng_seed_int(window.screenY);
}
function rng_get_byte() {
if(rng_state == null) {
rng_seed_time();
rng_state = prng_newstate();
rng_state.init(rng_pool);
for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
rng_pool[rng_pptr] = 0;
rng_pptr = 0;
//rng_pool = null;
}
// TODO: allow reseeding after first request
return rng_state.next();
}
function rng_get_bytes(ba) {
var i;
for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
}
function SecureRandom() {}
SecureRandom.prototype.nextBytes = rng_get_bytes;
/* eslint-disable */
// Depends on jsbn.js and rng.js
// Version 1.1: support utf-8 encoding in pkcs1pad2
import './jsbn';
import './rng';
import './base64';
const stringToUtf8ByteArray = (str) => {
// TODO(user): Use native implementations if/when available
const out = [];
let p = 0;
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
if (c < 128) {
out[p++] = c;
} else if (c < 2048) {
out[p++] = (c >> 6) | 192;
out[p++] = (c & 63) | 128;
} else if (
((c & 0xFC00) === 0xD800) && (i + 1) < str.length &&
((str.charCodeAt(i + 1) & 0xFC00) === 0xDC00)) {
// Surrogate Pair
c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF);
out[p++] = (c >> 18) | 240;
out[p++] = ((c >> 12) & 63) | 128;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
} else {
out[p++] = (c >> 12) | 224;
out[p++] = ((c >> 6) & 63) | 128;
out[p++] = (c & 63) | 128;
}
}
return out;
};
// convert a (hex) string to a bignum object
function parseBigInt(str,r) {
return new BigInteger(str,r);
}
function linebrk(s,n) {
var ret = "";
var i = 0;
while(i + n < s.length) {
ret += s.substring(i,i+n) + "\n";
i += n;
}
return ret + s.substring(i,s.length);
}
function byte2Hex(b) {
if(b < 0x10)
return "0" + b.toString(16);
else
return b.toString(16);
}
function str2blocks(s, n) {
const bytes = stringToUtf8ByteArray(s);
const blocks = [];
const blockSize = n - 11;
for (let i = 1; i * blockSize < bytes.length; ++i) {
blocks.push(pkcs1pad(bytes.slice((i - 1) * blockSize, i * blockSize), n));
}
return blocks;
}
function pkcs1pad(block, n) {
const out = block;
out.push(0);
const rng = new SecureRandom();
const x = [];
const rSize = n - out.length - 3;
for (let i = 0; i < rSize; ++i) {
x[0] = 0;
while(x[0] === 0) rng.nextBytes(x);
out.push(x[0]);
}
out.push(2);
out.push(0);
return new BigInteger(out);
}
// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s,n) {
if(n < s.length + 11) { // TODO: fix for utf-8
alert("Message too long for RSA");
return null;
}
var ba = new Array();
var i = s.length - 1;
while(i >= 0 && n > 0) {
var c = s.charCodeAt(i--);
if(c < 128) { // encode using utf-8
ba[--n] = c;
}
else if((c > 127) && (c < 2048)) {
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;
}
else {
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;
}
}
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
while(n > 2) { // random non-zero pad
x[0] = 0;
while(x[0] == 0) rng.nextBytes(x);
ba[--n] = x[0];
}
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);
}
// "empty" RSA key constructor
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
// Set the public key fields N and e from hex strings
function RSASetPublic(N,E) {
if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
}
else
alert("Invalid RSA public key");
}
// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}
// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
}
function RSAEncryptLongText(text) {
const blocks = str2blocks(text, (this.n.bitLength()+7)>>3);
const out = [];
for (let i = 0; i < blocks.length; ++ i) {
const c = this.doPublic(blocks[i]);
if (c == null) return null;
const h = c.toString(16);
if ((h.length & 1) === 0) {
out.push(h);
} else {
out.push(`0${h}`);
}
}
return out.join('');
}
// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
//function RSAEncryptB64(text) {
// var h = this.encrypt(text);
// if(h) return hex2b64(h); else return null;
//}
// protected
RSAKey.prototype.doPublic = RSADoPublic;
// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
RSAKey.prototype.encryptLong = RSAEncryptLongText;
//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;
export default RSAKey;
This source diff could not be displayed because it is too large. You can view the blob instead.
/* 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 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
* @param value
* @return key
*/
/**
* 获取用于对比的key
* @param value
* @param {string|KeyExtractor} [keyMethod]
* @return {*}
*/
const getKey = (value, keyMethod) => {
if (!keyMethod) {
return value;
} else if (isString(keyMethod)) {
return get(value, keyMethod);
} else {
return keyMethod(value);
}
};
/**
* 在数组中查找某个对象,并返回
* @param {Array} array
* @param toFind 查找目标
* @param {string|KeyExtractor} [key] 用于判断2个数据是否相同
* @return result 找到的对象
*/
const findBy = (array, toFind, key) => {
if (array) {
for (const el of array) {
if (getKey(el, key) === toFind) {
return el;
}
}
}
return null;
};
/**
* 计算频率数据
* @param {Array} array 数据
* @param {string|KeyExtractor} [key] 用于判断2个数据是否相同
* @param {string} [order=desc] 怎么排序,可选值为asc和desc
*/
export const toHistogram = (array, key, order = 'desc') => {
if (!array || array.length === 0) {
return [];
}
const res = [];
let newKey;
if (!key) {
newKey = 'data';
} else if (isString(key)) {
newKey = `data.${key}`;
} else {
newKey = value => key(value.data);
}
for (const el of array) {
const find = findBy(res, getKey(el, key), newKey);
if (find) {
++find.num;
} else {
res.push({
data: el,
num: 1,
});
}
}
switch (order) {
case 'desc':
return flow(
sortBy(v => v.num),
reverse,
)(res);
case 'asc':
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);
};
/* eslint-disable no-unused-vars */
import { getToken } from './auth';
import { encrypt } from './helper';
import { errors } from './error';
const putTokenOnUrl = async (url, params, options) => {
let token = await getToken();
if (!token) {
throw errors.tokenMissing();
}
token = encrypt(token);
return {
url,
options,
params: [...params, ['token', token]],
};
};
const putTokenToBody = async (url, data, params, options) => {
let token = await getToken();
if (!token) {
throw errors.tokenMissing();
}
token = encrypt(token);
return {
url,
params,
options,
data: {
token,
...data,
},
};
};
const parseResponse = (response) => {
const { errorCode, data, message } = response;
if (errorCode === 0) {
return data;
} else {
const error = new Error(message || data);
error.data = response;
throw error;
}
};
export default {
get: {
onRequest: async (url, params, options, auth) => {
if (auth) {
return putTokenOnUrl(url, params, options);
} else {
return {
url,
options,
params,
};
}
},
onResponse: parseResponse,
},
post: {
onRequest: async (url, data, params, options, auth) => {
if (auth) {
return putTokenToBody(url, data, params, options);
} else {
return {
url,
params,
options,
data,
};
}
},
onResponse: parseResponse,
},
delete: {
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 from 'lodash/isNil';
import defaults from 'lodash/defaults';
import { fetch } from './polyfill';
import { checkStatus, normParams, parseObject } from './http-helper';
import middleware from './middleware';
const defaultOptions = {
headers: {
Accept: 'application/json',
},
};
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 = {};
}
params = normParams(params);
const res = await middleware.post.onRequest(url, data, params, options, auth);
let queryParams = res ? res.params : params;
queryParams = queryParams.map(([k, v]) => (isNil(v) ? k : `${k}=${encodeURIComponent(v)}`));
queryParams = queryParams.join('&');
let realUrl = res ? res.url : url;
if (queryParams) {
realUrl = `${url}?${queryParams}`;
}
const realOptions = defaults(res ? res.options : options, defaultOptions);
if (!realOptions.headers) {
realOptions.headers = {
Accept: 'application/json',
};
}
realOptions.headers['Content-Type'] = 'application/json';
realOptions.method = 'POST';
const body = res ? res.data : data;
realOptions.body = orderedStringify(body);
return fetch(realUrl, realOptions)
.then(checkStatus)
.then(resp => parseObject(resp, middleware.post.onResponse));
}
export const makeCancelable = (promise, silence = true) => {
let hasCanceled = false;
const canceledError = { isCanceled: true };
const wrappedPromise = new Promise((resolve, reject) => {
promise.then(
val => (hasCanceled ? reject(canceledError) : resolve(val)),
error => (hasCanceled ? reject(canceledError) : reject(error)),
);
});
return {
run: cb => cb(wrappedPromise).catch((err) => {
if (silence && err !== canceledError) {
throw err;
}
}),
cancel() {
hasCanceled = true;
},
};
};
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' },
};
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param auth
* @param params
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default async function request(url, params = {}, options = {}, auth = true) {
const normedParams = normParams(params);
const res = await middleware.get.onRequest(url, normedParams, options, auth);
const newUrl = res ? res.url : url;
const newParams = res ? res.params : normedParams;
const newOptions = res ? res.options : options;
let queryParams = newParams.map(([k, v]) => (isNil(v) ? k : `${k}=${encodeURIComponent(v)}`));
queryParams = queryParams.join('&');
let realUrl = newUrl;
if (queryParams) {
realUrl = `${url}?${queryParams}`;
}
return fetch(realUrl, defaults(newOptions, defaultOptions))
.then(checkStatus)
.then(resp => parseObject(resp, middleware.get.onResponse));
}
export function years(value) {
return [
'y', value,
];
}
export function quarters(value) {
return [
'Q', value,
];
}
export function months(value) {
return [
'M', value,
];
}
export function weeks(value) {
return [
'w', value,
];
}
export function days(value) {
return [
'd', value,
];
}
export function hours(value) {
return [
'h', value,
];
}
export function minutes(value) {
return [
'm', value,
];
}
export function seconds(value) {
return [
's', value,
];
}
export function milliseconds(value) {
return [
'ms', value,
];
}
import AXO from 'axo';
import bowser from 'bowser';
export const isBrowserSupport = () => {
return bowser.msie;
};
/**
* 初始化
* @return {number} 0: success; 1: blocked by the browser; 2: driver not installed
*/
export const init = () => {
if (bowser.msie) {
try {
const se = new AXO('SafeEngineCOM.SafeEngineCtl');
if (se) {
return 0;
} else {
return 2;
}
} catch (err) {
return 1;
}
} else {
return 3;
}
};
/**
* 获取证书
* @param password usb-key的密钥
* @return {{[cert]: string, [deadTime]: number, status: number, [error]: string}}
*/
export const getCert = (password) => {
if (bowser.msie) {
const safeEngine = new AXO('SafeEngineCOM.SafeEngineCtl');
if (!safeEngine) {
return {
status: 1,
input: password,
error: '未安装USB证书控件。',
};
}
safeEngine.SEH_InitialSession(27, 'com1', password, 100, 2, '', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: 'USB-KEY初始化失败,请确认USB-KEY是否插入或密码是否正确。',
};
}
try {
const cert = safeEngine.SEH_GetSelfCertificate(27, 'com1', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: `获取个人证书错误,错误代码为:${safeEngine.ErrorCode}。`,
};
}
const res = {
status: 0,
cert,
input: password,
};
const deadTime = safeEngine.SEH_GetCertValidDate(cert);
res.deadTime = parseInt(deadTime, 10);
return res;
} finally {
safeEngine.SEH_ClearSession();
}
} else {
return {
status: 1,
error: '当前浏览器不支持activeX控件,请切换ie或带ie内核的360之类的浏览器。',
};
}
};
/**
* 签名
* @param password usb-key的密钥
* @param data 需要签名的数据,从服务端获取
* @return {{input: string, [cert]: string, [deadTime]: number, [signed]: string, status: number, [error]: string}}
*/
export const sign = (password, data) => {
if (bowser.msie || bowser.msedge) {
const safeEngine = new AXO('SafeEngineCOM.SafeEngineCtl');
if (!safeEngine) {
return {
status: 1,
input: password,
error: '未安装USB证书控件。',
};
}
safeEngine.SEH_InitialSession(27, 'com1', password, 100, 2, '', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: 'USB-KEY初始化失败,请确认USB-KEY是否插入或密码是否正确。',
};
}
try {
const cert = safeEngine.SEH_GetSelfCertificate(27, 'com1', '');
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: `获取个人证书错误,错误代码为:${safeEngine.ErrorCode}。`,
};
}
const res = { cert, status: 0, input: password };
const deadTime = safeEngine.SEH_GetCertValidDate(cert);
res.deadTime = parseInt(deadTime, 10);
const signed = safeEngine.SEH_SignData(data, 3);
if (safeEngine.ErrorCode !== 0) {
return {
status: safeEngine.ErrorCode,
input: password,
error: `数字签名失败,错误代码为:${safeEngine.ErrorCode}。`,
};
}
res.signed = signed;
return res;
} finally {
safeEngine.SEH_ClearSession();
}
} else {
return {
status: 1,
error: '当前浏览器不支持activeX控件,请切换ie或带ie内核的360之类的浏览器。',
};
}
};
/* eslint-disable no-console */
import React from 'react';
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 DsTable from '../src/components/table/dstable';
import TableInput from '../src/components/table/input-table';
import UCA from '../src/components/uca';
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', '/']))(dva()(DsTable));
const coordinate = {
containerType: 'module',
containerName: 'test',
datasourceName: 'QueryLeftHousesReturnTable',
};
return <Comp coordinate={coordinate} />;
});
const RangePicker = DatePicker.RangePicker;
storiesOf('antd', module)
.add('RangePicker', withInfo({ inline: true })(() => {
return <RangePicker />;
}))
.add('uca', withInfo({ inline: true })(() => {
return <UCA onChange={evt => console.log(evt)} />;
}));
storiesOf('table-input', module)
.add('1', () => {
const Temp = ({ form }) => {
const props = {
items: [
{
name: 'item0',
label: 'test0',
},
{
name: 'item1',
label: 'test1',
},
],
};
const handleSubmit = (e) => {
e.preventDefault();
form.validateFields((err, values) => {
if (!err) {
Modal.info({
title: '提交内容',
content: <ReactJson src={values} />,
});
}
});
};
return (
<Form onSubmit={handleSubmit}>
{
form.getFieldDecorator('table-input')(
<TableInput {...props}>
<Input />
<Input />
</TableInput>,
)
}
<Form.Item>
<Button htmlType="submit">提交</Button>
</Form.Item>
</Form>
);
};
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)
/* eslint-disable no-undef */
/**
* Created by yaohx_169 on 2017/6/29.
*/
import chai from 'chai';
chai.should();
describe('ioc', () => {
describe('stateful', () => {
});
});
const { JSDOM } = require('jsdom');
const jsdom = new JSDOM('<!doctype html><html><body/></html>');
const { window } = jsdom;
window.matchMedia = window.matchMedia || function matchMedia() {
return {
matches: false,
addListener() {},
removeListener() {},
};
};
function copyProps(src, target) {
const props = Object.getOwnPropertyNames(src)
.filter(prop => typeof target[prop] === 'undefined')
.map(prop => Object.getOwnPropertyDescriptor(src, prop));
Object.defineProperties(target, props);
}
global.window = window;
global.document = window.document;
global.navigator = {
userAgent: 'node.js',
};
copyProps(window, global);
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;
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论