feat: add .eslintrc and format all code (#11)
This commit is contained in:
parent
7c56c63893
commit
3aa25da75b
|
@ -0,0 +1,112 @@
|
|||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
},
|
||||
"requireConfigFile": false,
|
||||
"babelOptions": {
|
||||
"babelrc": false,
|
||||
"configFile": false,
|
||||
"presets": ["@babel/preset-react"]
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"plugins": ["unused-imports"],
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
"rules": {
|
||||
"semi": ["error", "always"],
|
||||
"indent": ["error", 2],
|
||||
// follow antd's style guide
|
||||
"quotes": ["error", "double"],
|
||||
"jsx-quotes": ["error", "prefer-double"],
|
||||
"space-in-parens": ["error", "never"],
|
||||
"object-curly-spacing": ["error", "never"],
|
||||
"array-bracket-spacing": ["error", "never"],
|
||||
"comma-spacing": ["error", { "before": false, "after": true }],
|
||||
"react/jsx-curly-spacing": [
|
||||
"error",
|
||||
{ "when": "never", "allowMultiline": true, "children": true }
|
||||
],
|
||||
"arrow-spacing": ["error", { "before": true, "after": true }],
|
||||
"space-before-blocks": ["error", "always"],
|
||||
"spaced-comment": ["error", "always"],
|
||||
"react/jsx-tag-spacing": ["error", { "beforeSelfClosing": "always" }],
|
||||
"block-spacing": ["error", "never"],
|
||||
"space-before-function-paren": ["error", "never"],
|
||||
"no-trailing-spaces": ["error", { "ignoreComments": true }],
|
||||
"eol-last": ["error", "always"],
|
||||
"no-var": ["error"],
|
||||
"prefer-const": [
|
||||
"error",
|
||||
{
|
||||
"destructuring": "all"
|
||||
}
|
||||
],
|
||||
"curly": ["error", "all"],
|
||||
"brace-style": ["error", "1tbs", { "allowSingleLine": true }],
|
||||
"no-mixed-spaces-and-tabs": "error",
|
||||
"sort-imports": [
|
||||
"error",
|
||||
{
|
||||
"ignoreDeclarationSort": true
|
||||
}
|
||||
],
|
||||
"no-multiple-empty-lines": [
|
||||
"error",
|
||||
{ "max": 1, "maxBOF": 0, "maxEOF": 0 }
|
||||
],
|
||||
"space-unary-ops": ["error", { "words": true, "nonwords": false }],
|
||||
"space-infix-ops": "error",
|
||||
"key-spacing": ["error", { "beforeColon": false, "afterColon": true }],
|
||||
"comma-style": ["error", "last"],
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
{
|
||||
"arrays": "always-multiline",
|
||||
"objects": "always-multiline",
|
||||
"imports": "never",
|
||||
"exports": "never",
|
||||
"functions": "never"
|
||||
}
|
||||
],
|
||||
"no-multi-spaces": ["error", { "ignoreEOLComments": true }],
|
||||
"react/no-unknown-property": [
|
||||
"error",
|
||||
{
|
||||
"ignore": ["css"]
|
||||
}
|
||||
],
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "none",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"no-unused-vars": "off",
|
||||
"react/no-deprecated": "error",
|
||||
"react/jsx-key": "error",
|
||||
"no-console": "error",
|
||||
"eqeqeq": "error",
|
||||
"keyword-spacing": "error",
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"no-case-declarations": "off"
|
||||
}
|
||||
}
|
141
web/src/App.js
141
web/src/App.js
|
@ -1,8 +1,8 @@
|
|||
import React, {Component} from 'react';
|
||||
import {Switch, Redirect, Route, withRouter, Link} from 'react-router-dom';
|
||||
import {Avatar, BackTop, Dropdown, Layout, Menu} from 'antd';
|
||||
import {createFromIconfontCN, DownOutlined, LogoutOutlined, SettingOutlined} from '@ant-design/icons';
|
||||
import './App.less';
|
||||
import React, {Component} from "react";
|
||||
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||
import {Avatar, BackTop, Dropdown, Layout, Menu} from "antd";
|
||||
import {DownOutlined, LogoutOutlined, SettingOutlined, createFromIconfontCN} from "@ant-design/icons";
|
||||
import "./App.less";
|
||||
import * as Setting from "./Setting";
|
||||
import * as AccountBackend from "./backend/AccountBackend";
|
||||
import AuthCallback from "./AuthCallback";
|
||||
|
@ -25,7 +25,7 @@ import i18next from "i18next";
|
|||
const {Header, Footer} = Layout;
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: '//at.alicdn.com/t/font_2680620_ffij16fkwdg.js',
|
||||
scriptUrl: "//at.alicdn.com/t/font_2680620_ffij16fkwdg.js",
|
||||
});
|
||||
|
||||
class App extends Component {
|
||||
|
@ -61,32 +61,32 @@ class App extends Component {
|
|||
this.setState({
|
||||
uri: uri,
|
||||
});
|
||||
if (uri === '/home') {
|
||||
this.setState({selectedMenuKey: '/home'});
|
||||
} else if (uri.includes('/stores')) {
|
||||
this.setState({ selectedMenuKey: '/stores' });
|
||||
} else if (uri.includes('/clustering')) {
|
||||
this.setState({ selectedMenuKey: '/clustering' });
|
||||
} else if (uri.includes('/wordsets')) {
|
||||
this.setState({ selectedMenuKey: '/wordsets' });
|
||||
} else if (uri.includes('/vectorsets')) {
|
||||
this.setState({ selectedMenuKey: '/vectorsets' });
|
||||
} else if (uri.includes('/videos')) {
|
||||
this.setState({ selectedMenuKey: '/videos' });
|
||||
if (uri === "/home") {
|
||||
this.setState({selectedMenuKey: "/home"});
|
||||
} else if (uri.includes("/stores")) {
|
||||
this.setState({selectedMenuKey: "/stores"});
|
||||
} else if (uri.includes("/clustering")) {
|
||||
this.setState({selectedMenuKey: "/clustering"});
|
||||
} else if (uri.includes("/wordsets")) {
|
||||
this.setState({selectedMenuKey: "/wordsets"});
|
||||
} else if (uri.includes("/vectorsets")) {
|
||||
this.setState({selectedMenuKey: "/vectorsets"});
|
||||
} else if (uri.includes("/videos")) {
|
||||
this.setState({selectedMenuKey: "/videos"});
|
||||
} else {
|
||||
this.setState({selectedMenuKey: 'null'});
|
||||
this.setState({selectedMenuKey: "null"});
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateAccount(account) {
|
||||
this.setState({
|
||||
account: account
|
||||
account: account,
|
||||
});
|
||||
}
|
||||
|
||||
setLanguage(account) {
|
||||
// let language = account?.language;
|
||||
let language = localStorage.getItem("language");
|
||||
const language = localStorage.getItem("language");
|
||||
if (language !== "" && language !== i18next.language) {
|
||||
Setting.setLanguage(language);
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class App extends Component {
|
|||
getAccount() {
|
||||
AccountBackend.getAccount()
|
||||
.then((res) => {
|
||||
let account = res.data;
|
||||
const account = res.data;
|
||||
if (account !== null) {
|
||||
this.setLanguage(account);
|
||||
}
|
||||
|
@ -109,12 +109,12 @@ class App extends Component {
|
|||
signout() {
|
||||
AccountBackend.signout()
|
||||
.then((res) => {
|
||||
if (res.status === 'ok') {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
account: null
|
||||
account: null,
|
||||
});
|
||||
|
||||
Setting.showMessage("success", `Successfully signed out, redirected to homepage`);
|
||||
Setting.showMessage("success", "Successfully signed out, redirected to homepage");
|
||||
Setting.goToLink("/");
|
||||
// this.props.history.push("/");
|
||||
} else {
|
||||
|
@ -124,9 +124,9 @@ class App extends Component {
|
|||
}
|
||||
|
||||
handleRightDropdownClick(e) {
|
||||
if (e.key === '/account') {
|
||||
if (e.key === "/account") {
|
||||
Setting.openLink(Setting.getMyProfileUrl(this.state.account));
|
||||
} else if (e.key === '/logout') {
|
||||
} else if (e.key === "/logout") {
|
||||
this.signout();
|
||||
}
|
||||
}
|
||||
|
@ -134,16 +134,16 @@ class App extends Component {
|
|||
renderAvatar() {
|
||||
if (this.state.account.avatar === "") {
|
||||
return (
|
||||
<Avatar style={{ backgroundColor: Setting.getAvatarColor(this.state.account.name), verticalAlign: 'middle' }} size="large">
|
||||
<Avatar style={{backgroundColor: Setting.getAvatarColor(this.state.account.name), verticalAlign: "middle"}} size="large">
|
||||
{Setting.getShortName(this.state.account.name)}
|
||||
</Avatar>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Avatar src={this.state.account.avatar} style={{verticalAlign: 'middle' }} size="large">
|
||||
<Avatar src={this.state.account.avatar} style={{verticalAlign: "middle"}} size="large">
|
||||
{Setting.getShortName(this.state.account.name)}
|
||||
</Avatar>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ class App extends Component {
|
|||
|
||||
return (
|
||||
<Dropdown key="/rightDropDown" overlay={menu} className="rightDropDown">
|
||||
<div className="ant-dropdown-link" style={{float: 'right', cursor: 'pointer'}}>
|
||||
<div className="ant-dropdown-link" style={{float: "right", cursor: "pointer"}}>
|
||||
|
||||
|
||||
{
|
||||
|
@ -177,24 +177,24 @@ class App extends Component {
|
|||
|
||||
</div>
|
||||
</Dropdown>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderAccount() {
|
||||
let res = [];
|
||||
const res = [];
|
||||
|
||||
if (this.state.account === undefined) {
|
||||
return null;
|
||||
} else if (this.state.account === null) {
|
||||
res.push(
|
||||
<Menu.Item key="/signup" style={{float: 'right', marginRight: '20px'}}>
|
||||
<Menu.Item key="/signup" style={{float: "right", marginRight: "20px"}}>
|
||||
<a href={Setting.getSignupUrl()}>
|
||||
{i18next.t("account:Sign Up")}
|
||||
</a>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="/signin" style={{float: 'right'}}>
|
||||
<Menu.Item key="/signin" style={{float: "right"}}>
|
||||
<a href={Setting.getSigninUrl()}>
|
||||
{i18next.t("account:Sign In")}
|
||||
</a>
|
||||
|
@ -203,19 +203,19 @@ class App extends Component {
|
|||
} else {
|
||||
res.push(this.renderRightDropdown());
|
||||
return (
|
||||
<div style={{ float: 'right', margin: '0px', padding: '0px'}}>
|
||||
<div style={{float: "right", margin: "0px", padding: "0px"}}>
|
||||
{
|
||||
res
|
||||
}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
let res = [];
|
||||
const res = [];
|
||||
|
||||
if (this.state.account === null || this.state.account === undefined) {
|
||||
return [];
|
||||
|
@ -298,7 +298,7 @@ class App extends Component {
|
|||
|
||||
renderHomeIfSignedIn(component) {
|
||||
if (this.state.account !== null && this.state.account !== undefined) {
|
||||
return <Redirect to='/' />
|
||||
return <Redirect to="/" />;
|
||||
} else {
|
||||
return component;
|
||||
}
|
||||
|
@ -307,11 +307,10 @@ class App extends Component {
|
|||
renderSigninIfNotSignedIn(component) {
|
||||
if (this.state.account === null) {
|
||||
sessionStorage.setItem("from", window.location.pathname);
|
||||
return <Redirect to='/signin' />
|
||||
return <Redirect to="/signin" />;
|
||||
} else if (this.state.account === undefined) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
@ -319,7 +318,7 @@ class App extends Component {
|
|||
renderContent() {
|
||||
return (
|
||||
<div>
|
||||
<Header style={{padding: '0', marginBottom: '3px'}}>
|
||||
<Header style={{padding: "0", marginBottom: "3px"}}>
|
||||
{
|
||||
Setting.isMobile() ? null : (
|
||||
<Link to={"/"}>
|
||||
|
@ -331,7 +330,7 @@ class App extends Component {
|
|||
// theme="dark"
|
||||
mode={"horizontal"}
|
||||
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
||||
style={{lineHeight: '64px'}}
|
||||
style={{lineHeight: "64px"}}
|
||||
>
|
||||
{
|
||||
this.renderMenu()
|
||||
|
@ -339,8 +338,8 @@ class App extends Component {
|
|||
{
|
||||
this.renderAccount()
|
||||
}
|
||||
<Menu.Item key="en" className="rightDropDown" style={{float: 'right', cursor: 'pointer', marginLeft: '-10px', marginRight: '20px'}}>
|
||||
<div className="rightDropDown" style={{float: 'right', cursor: 'pointer'}} onClick={() => {Setting.changeLanguage("en");}}>
|
||||
<Menu.Item key="en" className="rightDropDown" style={{float: "right", cursor: "pointer", marginLeft: "-10px", marginRight: "20px"}}>
|
||||
<div className="rightDropDown" style={{float: "right", cursor: "pointer"}} onClick={() => {Setting.changeLanguage("en");}}>
|
||||
<IconFont type="icon-en" />
|
||||
|
||||
English
|
||||
|
@ -348,8 +347,8 @@ class App extends Component {
|
|||
|
||||
</div>
|
||||
</Menu.Item>
|
||||
<Menu.Item key="zh" className="rightDropDown" style={{float: 'right', cursor: 'pointer'}}>
|
||||
<div className="rightDropDown" style={{float: 'right', cursor: 'pointer'}} onClick={() => {Setting.changeLanguage("zh");}}>
|
||||
<Menu.Item key="zh" className="rightDropDown" style={{float: "right", cursor: "pointer"}}>
|
||||
<div className="rightDropDown" style={{float: "right", cursor: "pointer"}} onClick={() => {Setting.changeLanguage("zh");}}>
|
||||
<IconFont type="icon-zh" />
|
||||
|
||||
中文
|
||||
|
@ -360,24 +359,24 @@ class App extends Component {
|
|||
</Menu>
|
||||
</Header>
|
||||
<Switch>
|
||||
<Route exact path="/callback" component={AuthCallback}/>
|
||||
<Route exact path="/signin" render={(props) => this.renderHomeIfSignedIn(<SigninPage {...props} />)}/>
|
||||
<Route exact path="/" render={(props) => this.renderSigninIfNotSignedIn(<HomePage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/home" render={(props) => this.renderSigninIfNotSignedIn(<HomePage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/stores" render={(props) => this.renderSigninIfNotSignedIn(<StoreListPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/stores/:owner/:storeName" render={(props) => this.renderSigninIfNotSignedIn(<StoreEditPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/stores/:owner/:storeName/view" render={(props) => this.renderSigninIfNotSignedIn(<FileTreePage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/clustering" render={(props) => this.renderSigninIfNotSignedIn(<ClusteringPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/wordsets" render={(props) => this.renderSigninIfNotSignedIn(<WordsetListPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/wordsets/:wordsetName" render={(props) => this.renderSigninIfNotSignedIn(<WordsetEditPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/wordsets/:wordsetName/graph" render={(props) => this.renderSigninIfNotSignedIn(<WordsetGraphPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/vectorsets" render={(props) => this.renderSigninIfNotSignedIn(<VectorsetListPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/vectorsets/:vectorsetName" render={(props) => this.renderSigninIfNotSignedIn(<VectorsetEditPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/videos" render={(props) => this.renderSigninIfNotSignedIn(<VideoListPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/videos/:videoName" render={(props) => this.renderSigninIfNotSignedIn(<VideoEditPage account={this.state.account} {...props} />)}/>
|
||||
<Route exact path="/callback" component={AuthCallback} />
|
||||
<Route exact path="/signin" render={(props) => this.renderHomeIfSignedIn(<SigninPage {...props} />)} />
|
||||
<Route exact path="/" render={(props) => this.renderSigninIfNotSignedIn(<HomePage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/home" render={(props) => this.renderSigninIfNotSignedIn(<HomePage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/stores" render={(props) => this.renderSigninIfNotSignedIn(<StoreListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/stores/:owner/:storeName" render={(props) => this.renderSigninIfNotSignedIn(<StoreEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/stores/:owner/:storeName/view" render={(props) => this.renderSigninIfNotSignedIn(<FileTreePage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/clustering" render={(props) => this.renderSigninIfNotSignedIn(<ClusteringPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/wordsets" render={(props) => this.renderSigninIfNotSignedIn(<WordsetListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/wordsets/:wordsetName" render={(props) => this.renderSigninIfNotSignedIn(<WordsetEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/wordsets/:wordsetName/graph" render={(props) => this.renderSigninIfNotSignedIn(<WordsetGraphPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/vectorsets" render={(props) => this.renderSigninIfNotSignedIn(<VectorsetListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/vectorsets/:vectorsetName" render={(props) => this.renderSigninIfNotSignedIn(<VectorsetEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/videos" render={(props) => this.renderSigninIfNotSignedIn(<VideoListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/videos/:videoName" render={(props) => this.renderSigninIfNotSignedIn(<VideoEditPage account={this.state.account} {...props} />)} />
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
|
@ -387,20 +386,20 @@ class App extends Component {
|
|||
return (
|
||||
<Footer id="footer" style={
|
||||
{
|
||||
borderTop: '1px solid #e8e8e8',
|
||||
backgroundColor: 'white',
|
||||
textAlign: 'center',
|
||||
borderTop: "1px solid #e8e8e8",
|
||||
backgroundColor: "white",
|
||||
textAlign: "center",
|
||||
}
|
||||
}>
|
||||
Made with <span style={{color: 'rgb(255, 255, 255)'}}>❤️</span> by <a style={{fontWeight: "bold", color: "black"}} target="_blank" rel="noreferrer" href="https://github.com/casbin/casibase">Casibase</a>, { Setting.isMobile() ? "Mobile" : "Desktop" } View
|
||||
Made with <span style={{color: "rgb(255, 255, 255)"}}>❤️</span> by <a style={{fontWeight: "bold", color: "black"}} target="_blank" rel="noreferrer" href="https://github.com/casbin/casibase">Casibase</a>, {Setting.isMobile() ? "Mobile" : "Desktop"} View
|
||||
</Footer>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="parent-area">
|
||||
<BackTop/>
|
||||
<BackTop />
|
||||
<div id="content-wrap">
|
||||
{
|
||||
this.renderContent()
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import App from "./App";
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
it("renders without crashing", () => {
|
||||
const div = document.createElement("div");
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { Button, Result, Spin } from "antd";
|
||||
import { withRouter } from "react-router-dom";
|
||||
import {Button, Result, Spin} from "antd";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class AuthCallback extends React.Component {
|
||||
|
@ -27,7 +27,7 @@ class AuthCallback extends React.Component {
|
|||
login() {
|
||||
Setting.signin().then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", `Logged in successfully`)
|
||||
Setting.showMessage("success", "Logged in successfully");
|
||||
|
||||
const link = this.getFromLink();
|
||||
Setting.goToLink(link);
|
||||
|
@ -41,15 +41,15 @@ class AuthCallback extends React.Component {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ textAlign: "center" }}>
|
||||
<div style={{textAlign: "center"}}>
|
||||
{this.state.msg === null ? (
|
||||
<Spin
|
||||
size="large"
|
||||
tip="Signing in..."
|
||||
style={{ paddingTop: "10%" }}
|
||||
style={{paddingTop: "10%"}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{ display: "inline" }}>
|
||||
<div style={{display: "inline"}}>
|
||||
<Result
|
||||
status="error"
|
||||
title="Login Error"
|
||||
|
|
|
@ -27,8 +27,8 @@ class ClusteringPage extends React.Component {
|
|||
|
||||
render() {
|
||||
return (this.state.wordset === undefined || this.state.wordset === null) ? null : (
|
||||
<WordsetGraph wordset={this.state.wordset} wordsetName={this.state.wordset.name}/>
|
||||
)
|
||||
<WordsetGraph wordset={this.state.wordset} wordsetName={this.state.wordset.name} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,4 +13,4 @@ export const DefaultWordsetName = "word";
|
|||
export const ForceLanguage = "";
|
||||
export const DefaultLanguage = "en";
|
||||
|
||||
export const AppUrl = ""
|
||||
export const AppUrl = "";
|
||||
|
|
|
@ -12,7 +12,7 @@ class DataChart extends React.Component {
|
|||
|
||||
componentWillMount() {
|
||||
fetch(this.props.url, {
|
||||
method: 'GET',
|
||||
method: "GET",
|
||||
}).then(res => res.text())
|
||||
.then((text) => {
|
||||
if (this.props.filename.startsWith("Impedance_")) {
|
||||
|
@ -34,7 +34,7 @@ class DataChart extends React.Component {
|
|||
lines = lines.filter((line, index) => index % 20 === 0);
|
||||
|
||||
let data = lines.map(line => {
|
||||
let res = [];
|
||||
const res = [];
|
||||
const tokens = line.split(" ");
|
||||
tokens.forEach((token, index) => {
|
||||
if (index !== 0) {
|
||||
|
@ -48,7 +48,7 @@ class DataChart extends React.Component {
|
|||
data = data.map(element => {
|
||||
element[0] = (new Date("0000-01-01 " + element[0]).getTime() - startTime) / 1000 / 60;
|
||||
return element;
|
||||
})
|
||||
});
|
||||
|
||||
this.setState({
|
||||
data: data,
|
||||
|
@ -62,11 +62,11 @@ class DataChart extends React.Component {
|
|||
|
||||
const option = {
|
||||
title: {
|
||||
text: 'Impedance'
|
||||
text: "Impedance",
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: function (params) {
|
||||
trigger: "axis",
|
||||
formatter: function(params) {
|
||||
return JSON.stringify(params.value);
|
||||
},
|
||||
axisPointer: {
|
||||
|
@ -74,31 +74,31 @@ class DataChart extends React.Component {
|
|||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
type: "value",
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
boundaryGap: [0, '100%'],
|
||||
type: "value",
|
||||
boundaryGap: [0, "100%"],
|
||||
splitLine: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Fake Data',
|
||||
type: 'line',
|
||||
name: "Fake Data",
|
||||
type: "line",
|
||||
showSymbol: false,
|
||||
data: this.state.data,
|
||||
},
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactEcharts style={{width: "100%", height: this.props.height, backgroundColor: "white"}} option={option} notMerge={true}/>
|
||||
)
|
||||
<ReactEcharts style={{width: "100%", height: this.props.height, backgroundColor: "white"}} option={option} notMerge={true} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {Button, Popconfirm, Table} from 'antd';
|
||||
import {Button, Popconfirm, Table} from "antd";
|
||||
import {DeleteOutlined, DownloadOutlined, FileDoneOutlined} from "@ant-design/icons";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
@ -38,7 +38,7 @@ class FileTable extends React.Component {
|
|||
}
|
||||
|
||||
addRow(table) {
|
||||
let row = {no: table.length, name: `New Vector - ${table.length}`, data: []};
|
||||
const row = {no: table.length, name: `New Vector - ${table.length}`, data: []};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class FileTable extends React.Component {
|
|||
FileBackend.deleteFile(storeId, file.key, isLeaf)
|
||||
.then((res) => {
|
||||
if (res === true) {
|
||||
Setting.showMessage("success", `File deleted successfully`);
|
||||
Setting.showMessage("success", "File deleted successfully");
|
||||
this.props.onRefresh();
|
||||
} else {
|
||||
Setting.showMessage("error", `File failed to delete: ${res}`);
|
||||
|
@ -81,19 +81,19 @@ class FileTable extends React.Component {
|
|||
const columns = [
|
||||
{
|
||||
title: i18next.t("vectorset:File name"),
|
||||
dataIndex: 'title',
|
||||
key: 'title',
|
||||
dataIndex: "title",
|
||||
key: "title",
|
||||
// width: '200px',
|
||||
sorter: (a, b) => a.title.localeCompare(b.title),
|
||||
render: (text, record, index) => {
|
||||
return text;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("store:Category"),
|
||||
dataIndex: 'isLeaf',
|
||||
key: 'isLeaf',
|
||||
width: '110px',
|
||||
dataIndex: "isLeaf",
|
||||
key: "isLeaf",
|
||||
width: "110px",
|
||||
sorter: (a, b) => a.isLeaf - b.isLeaf,
|
||||
filters: Setting.getDistinctArray(table.map(record => Setting.getFileCategory(record)))
|
||||
.map(fileType => {
|
||||
|
@ -102,13 +102,13 @@ class FileTable extends React.Component {
|
|||
onFilter: (value, record) => Setting.getFileCategory(record) === value,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFileCategory(record);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("store:File type"),
|
||||
dataIndex: 'fileType',
|
||||
key: 'fileType',
|
||||
width: '140px',
|
||||
dataIndex: "fileType",
|
||||
key: "fileType",
|
||||
width: "140px",
|
||||
sorter: (a, b) => Setting.getExtFromPath(a.title).localeCompare(Setting.getExtFromPath(b.title)),
|
||||
filters: Setting.getDistinctArray(table.map(record => Setting.getExtFromFile(record)))
|
||||
.filter(fileType => fileType !== "")
|
||||
|
@ -118,13 +118,13 @@ class FileTable extends React.Component {
|
|||
onFilter: (value, record) => Setting.getExtFromFile(record) === value,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getExtFromFile(record);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("vectorset:File size"),
|
||||
dataIndex: 'size',
|
||||
key: 'size',
|
||||
width: '120px',
|
||||
dataIndex: "size",
|
||||
key: "size",
|
||||
width: "120px",
|
||||
sorter: (a, b) => a.size - b.size,
|
||||
render: (text, record, index) => {
|
||||
if (!record.isLeaf) {
|
||||
|
@ -132,38 +132,38 @@ class FileTable extends React.Component {
|
|||
}
|
||||
|
||||
return Setting.getFriendlyFileSize(text);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Created time"),
|
||||
dataIndex: 'createdTime',
|
||||
key: 'createdTime',
|
||||
width: '160px',
|
||||
dataIndex: "createdTime",
|
||||
key: "createdTime",
|
||||
width: "160px",
|
||||
sorter: (a, b) => a.createdTime.localeCompare(b.createdTime),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFormattedDate(text);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("store:Collected time"),
|
||||
dataIndex: 'collectedTime',
|
||||
key: 'collectedTime',
|
||||
width: '160px',
|
||||
dataIndex: "collectedTime",
|
||||
key: "collectedTime",
|
||||
width: "160px",
|
||||
sorter: (a, b) => a.collectedTime.localeCompare(b.collectedTime),
|
||||
render: (text, record, index) => {
|
||||
const collectedTime = Setting.getCollectedTime(record.title);
|
||||
return Setting.getFormattedDate(collectedTime);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("store:Subject"),
|
||||
dataIndex: 'subject',
|
||||
key: 'subject',
|
||||
width: '90px',
|
||||
dataIndex: "subject",
|
||||
key: "subject",
|
||||
width: "90px",
|
||||
sorter: (a, b) => a.subject.localeCompare(b.subject),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getSubject(record.title);
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: i18next.t("store:Path"),
|
||||
|
@ -206,14 +206,14 @@ class FileTable extends React.Component {
|
|||
<Button icon={<DeleteOutlined />} style={{marginRight: "10px"}} type="primary" danger size="small">{i18next.t("store:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
<Button icon={<FileDoneOutlined />} size="small" onClick={() => {
|
||||
let fileKeys = [];
|
||||
const fileKeys = [];
|
||||
files.forEach((file, index) => {
|
||||
fileKeys.push(file.key);
|
||||
});
|
||||
PermissionUtil.addPermission(this.props.account, this.props.store, null, fileKeys);
|
||||
}}>{i18next.t("store:Add Permission")}</Button>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}} />
|
||||
);
|
||||
|
@ -226,7 +226,7 @@ class FileTable extends React.Component {
|
|||
this.renderTable(this.props.file.children)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import React from "react";
|
||||
import {Button, Col, DatePicker, Descriptions, Empty, Input, Modal, Popconfirm, Radio, Row, Select, Spin, Tooltip, Tree, Upload} from 'antd';
|
||||
import {CloudUploadOutlined, createFromIconfontCN, DeleteOutlined, DownloadOutlined, FileDoneOutlined, FolderAddOutlined, InfoCircleTwoTone} from "@ant-design/icons";
|
||||
import {Button, Col, DatePicker, Descriptions, Empty, Input, Modal, Popconfirm, Radio, Row, Select, Spin, Tooltip, Tree, Upload} from "antd";
|
||||
import {CloudUploadOutlined, DeleteOutlined, DownloadOutlined, FileDoneOutlined, FolderAddOutlined, InfoCircleTwoTone, createFromIconfontCN} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as FileBackend from "./backend/FileBackend";
|
||||
import DocViewer, { DocViewerRenderers } from "@cyntler/react-doc-viewer";
|
||||
import FileViewer from 'react-file-viewer';
|
||||
import DocViewer, {DocViewerRenderers} from "@cyntler/react-doc-viewer";
|
||||
import FileViewer from "react-file-viewer";
|
||||
import i18next from "i18next";
|
||||
import * as PermissionBackend from "./backend/PermissionBackend";
|
||||
import * as PermissionUtil from "./PermissionUtil";
|
||||
|
@ -17,11 +17,11 @@ import "codemirror/lib/codemirror.css";
|
|||
// require("codemirror/theme/material-darker.css");
|
||||
// require("codemirror/mode/javascript/javascript");
|
||||
|
||||
const { Search } = Input;
|
||||
const { Option } = Select;
|
||||
const {Search} = Input;
|
||||
const {Option} = Select;
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: 'https://cdn.open-ct.com/icon/iconfont.js',
|
||||
scriptUrl: "https://cdn.open-ct.com/icon/iconfont.js",
|
||||
});
|
||||
|
||||
class FileTree extends React.Component {
|
||||
|
@ -29,7 +29,7 @@ class FileTree extends React.Component {
|
|||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
expandedKeys: ['0-0', '0-0-0', '0-0-0-0'],
|
||||
expandedKeys: ["0-0", "0-0-0", "0-0-0-0"],
|
||||
checkedKeys: [],
|
||||
checkedFiles: [],
|
||||
selectedKeys: [],
|
||||
|
@ -55,7 +55,7 @@ class FileTree extends React.Component {
|
|||
}
|
||||
|
||||
getPermissionMap(permissions) {
|
||||
let permissionMap = {};
|
||||
const permissionMap = {};
|
||||
permissions.forEach((permission, index) => {
|
||||
if (permissionMap[permission.resources[0]] === undefined) {
|
||||
permissionMap[permission.resources[0]] = [];
|
||||
|
@ -116,7 +116,7 @@ class FileTree extends React.Component {
|
|||
console.log(this.state.file);
|
||||
console.log(this.state.info);
|
||||
|
||||
let newInfo = Setting.deepCopy(this.state.info);
|
||||
const newInfo = Setting.deepCopy(this.state.info);
|
||||
if (uploadFileType !== "Other") {
|
||||
for (let i = 0; i < newInfo.fileList.length; i++) {
|
||||
const filename = newInfo.fileList[i].name;
|
||||
|
@ -129,11 +129,11 @@ class FileTree extends React.Component {
|
|||
}} value={this.state.uploadFileType}>
|
||||
<Radio.Button value={"ECG"}>ECG</Radio.Button>
|
||||
<Radio.Button value={"Impedance"}>Impedance</Radio.Button>
|
||||
{/*<Radio.Button value={"EEG"}>EEG</Radio.Button>*/}
|
||||
{/* <Radio.Button value={"EEG"}>EEG</Radio.Button>*/}
|
||||
<Radio.Button value={"Other"}>{i18next.t("store:Other")}</Radio.Button>
|
||||
</Radio.Group>
|
||||
</Modal>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
uploadFile(file, info) {
|
||||
|
@ -152,19 +152,19 @@ class FileTree extends React.Component {
|
|||
|
||||
Promise.all(promises)
|
||||
.then((values) => {
|
||||
Setting.showMessage("success", `File uploaded successfully`);
|
||||
Setting.showMessage("success", "File uploaded successfully");
|
||||
this.props.onRefresh();
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `File failed to upload: ${error}`);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
addFile(file, newFolder) {
|
||||
const storeId = `${this.props.store.owner}/${this.props.store.name}`;
|
||||
FileBackend.addFile(storeId, file.key, false, newFolder, null)
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `File added successfully`);
|
||||
Setting.showMessage("success", "File added successfully");
|
||||
this.props.onRefresh();
|
||||
})
|
||||
.catch(error => {
|
||||
|
@ -177,7 +177,7 @@ class FileTree extends React.Component {
|
|||
FileBackend.deleteFile(storeId, file.key, isLeaf)
|
||||
.then((res) => {
|
||||
if (res === true) {
|
||||
Setting.showMessage("success", `File deleted successfully`);
|
||||
Setting.showMessage("success", "File deleted successfully");
|
||||
this.props.onRefresh();
|
||||
} else {
|
||||
Setting.showMessage("error", `File failed to delete: ${res}`);
|
||||
|
@ -212,11 +212,11 @@ class FileTree extends React.Component {
|
|||
Setting.getTag(username, permission.actions[0], permission.state)
|
||||
}
|
||||
</span>
|
||||
)
|
||||
);
|
||||
})
|
||||
}
|
||||
</span>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderPermissions(permissions, isReadable) {
|
||||
|
@ -263,11 +263,11 @@ class FileTree extends React.Component {
|
|||
}
|
||||
|
||||
isFileReadable(file) {
|
||||
return this.isFileOk(file, "Read")
|
||||
return this.isFileOk(file, "Read");
|
||||
}
|
||||
|
||||
isFileWritable(file) {
|
||||
return this.isFileOk(file, "Write")
|
||||
return this.isFileOk(file, "Write");
|
||||
}
|
||||
|
||||
isFileAdmin(file) {
|
||||
|
@ -275,7 +275,7 @@ class FileTree extends React.Component {
|
|||
return true;
|
||||
}
|
||||
|
||||
return this.isFileOk(file, "Admin")
|
||||
return this.isFileOk(file, "Admin");
|
||||
}
|
||||
|
||||
renderSearch() {
|
||||
|
@ -287,7 +287,7 @@ class FileTree extends React.Component {
|
|||
selectedFile: null,
|
||||
});
|
||||
}} />
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getCacheApp(filename) {
|
||||
|
@ -317,7 +317,7 @@ class FileTree extends React.Component {
|
|||
loading: true,
|
||||
});
|
||||
|
||||
fetch(url, {method: 'GET'})
|
||||
fetch(url, {method: "GET"})
|
||||
.then(res => res.text())
|
||||
.then(res => {
|
||||
this.setState({
|
||||
|
@ -327,7 +327,7 @@ class FileTree extends React.Component {
|
|||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const key = info.node.key;
|
||||
const filename = info.node.title;
|
||||
|
@ -418,18 +418,18 @@ class FileTree extends React.Component {
|
|||
{
|
||||
!isWritable ? null : (
|
||||
<React.Fragment>
|
||||
{/*<Tooltip title={i18next.t("store:Rename")}>*/}
|
||||
{/* <Tooltip title={i18next.t("store:Rename")}>*/}
|
||||
{/* <Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={(e) => {*/}
|
||||
{/* Setting.showMessage("error", "Rename");*/}
|
||||
{/* e.stopPropagation();*/}
|
||||
{/* }} />*/}
|
||||
{/*</Tooltip>*/}
|
||||
{/*<Tooltip title={i18next.t("store:Move")}>*/}
|
||||
{/* </Tooltip>*/}
|
||||
{/* <Tooltip title={i18next.t("store:Move")}>*/}
|
||||
{/* <Button style={{marginRight: "5px"}} icon={<RadiusSettingOutlined />} size="small" onClick={(e) => {*/}
|
||||
{/* Setting.showMessage("error", "Move");*/}
|
||||
{/* e.stopPropagation();*/}
|
||||
{/* }} />*/}
|
||||
{/*</Tooltip>*/}
|
||||
{/* </Tooltip>*/}
|
||||
<Tooltip title={i18next.t("store:Delete")}>
|
||||
<span onClick={(e) => e.stopPropagation()}>
|
||||
<Popconfirm
|
||||
|
@ -465,7 +465,7 @@ class FileTree extends React.Component {
|
|||
(this.state.permissionMap === null) ? null : this.renderPermissions(this.state.permissionMap[file.key], isReadable)
|
||||
}
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Tooltip color={"rgb(255,255,255,0.8)"} placement="right" title={
|
||||
|
@ -558,35 +558,35 @@ class FileTree extends React.Component {
|
|||
(this.state.permissionMap === null) ? null : this.renderPermissions(this.state.permissionMap[file.key], isReadable)
|
||||
}
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
}
|
||||
}}
|
||||
icon={(file) => {
|
||||
if (file.isLeaf) {
|
||||
const ext = Setting.getExtFromPath(file.data.key);
|
||||
if (ext === "pdf") {
|
||||
return <IconFont type='icon-testpdf' />
|
||||
return <IconFont type="icon-testpdf" />;
|
||||
} else if (ext === "doc" || ext === "docx") {
|
||||
return <IconFont type='icon-testdocx' />
|
||||
return <IconFont type="icon-testdocx" />;
|
||||
} else if (ext === "ppt" || ext === "pptx") {
|
||||
return <IconFont type='icon-testpptx' />
|
||||
return <IconFont type="icon-testpptx" />;
|
||||
} else if (ext === "xls" || ext === "xlsx") {
|
||||
return <IconFont type='icon-testxlsx' />
|
||||
return <IconFont type="icon-testxlsx" />;
|
||||
} else if (ext === "txt") {
|
||||
return <IconFont type='icon-testdocument' />
|
||||
return <IconFont type="icon-testdocument" />;
|
||||
} else if (ext === "png" || ext === "bmp" || ext === "jpg" || ext === "jpeg" || ext === "svg") {
|
||||
return <IconFont type='icon-testPicture' />
|
||||
return <IconFont type="icon-testPicture" />;
|
||||
} else if (ext === "html") {
|
||||
return <IconFont type='icon-testhtml' />
|
||||
return <IconFont type="icon-testhtml" />;
|
||||
} else if (ext === "js") {
|
||||
return <IconFont type='icon-testjs' />
|
||||
return <IconFont type="icon-testjs" />;
|
||||
} else if (ext === "css") {
|
||||
return <IconFont type='icon-testcss' />
|
||||
return <IconFont type="icon-testcss" />;
|
||||
} else {
|
||||
return <IconFont type='icon-testfile-unknown' />
|
||||
return <IconFont type="icon-testfile-unknown" />;
|
||||
}
|
||||
} else {
|
||||
return <IconFont type='icon-testfolder' />
|
||||
return <IconFont type="icon-testfolder" />;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -606,7 +606,7 @@ class FileTree extends React.Component {
|
|||
const outerFile = {children: this.state.checkedFiles};
|
||||
return (
|
||||
<FileTable account={this.props.account} store={this.props.store} onRefresh={() => this.props.onRefresh()} file={outerFile} isCheckMode={true} />
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.selectedKeys.length === 0) {
|
||||
|
@ -624,7 +624,7 @@ class FileTree extends React.Component {
|
|||
if (!file.isLeaf) {
|
||||
return (
|
||||
<FileTable account={this.props.account} store={this.props.store} onRefresh={() => this.props.onRefresh()} file={file} isCheckMode={false} />
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!filename.includes(".")) {
|
||||
|
@ -643,7 +643,7 @@ class FileTree extends React.Component {
|
|||
return (
|
||||
<iframe key={path} title={app} src={`${Conf.AppUrl}${app}`} width={"100%"} height={"100%"} />
|
||||
// <DataChart filename={filename} url={url} height={this.getEditorHeightCss()} />
|
||||
)
|
||||
);
|
||||
} else if (this.isExtForDocViewer(ext)) {
|
||||
// https://github.com/Alcumus/react-doc-viewer
|
||||
return (
|
||||
|
@ -665,8 +665,8 @@ class FileTree extends React.Component {
|
|||
header: {
|
||||
disableHeader: true,
|
||||
disableFileName: true,
|
||||
retainURLParams: false
|
||||
}
|
||||
retainURLParams: false,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
@ -718,7 +718,7 @@ class FileTree extends React.Component {
|
|||
}
|
||||
|
||||
setPropertyValue(file, propertyName, value) {
|
||||
let store = this.props.store;
|
||||
const store = this.props.store;
|
||||
if (store.propertiesMap[file.key] === undefined) {
|
||||
store.propertiesMap[file.key] = {};
|
||||
}
|
||||
|
@ -728,7 +728,7 @@ class FileTree extends React.Component {
|
|||
|
||||
getMomentTime(t) {
|
||||
if (t === "") {
|
||||
return ""
|
||||
return "";
|
||||
} else {
|
||||
return new moment(t);
|
||||
}
|
||||
|
@ -803,7 +803,7 @@ class FileTree extends React.Component {
|
|||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getEditorHeightCss() {
|
||||
|
@ -841,7 +841,7 @@ class FileTree extends React.Component {
|
|||
this.renderUploadFileModal()
|
||||
}
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,9 @@ class HomePage extends React.Component {
|
|||
|
||||
render() {
|
||||
if (this.props.account.tag === "Video") {
|
||||
return <Redirect to="/videos" />
|
||||
return <Redirect to="/videos" />;
|
||||
} else {
|
||||
return <FileTreePage account={this.props.account} />
|
||||
return <FileTreePage account={this.props.account} />;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {DeleteOutlined} from '@ant-design/icons';
|
||||
import {Button, Col, Input, InputNumber, Row, Table, Tooltip} from 'antd';
|
||||
import {DeleteOutlined} from "@ant-design/icons";
|
||||
import {Button, Col, Input, InputNumber, Row, Table, Tooltip} from "antd";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import FileSaver from "file-saver";
|
||||
|
@ -49,7 +49,7 @@ class LabelTable extends React.Component {
|
|||
return;
|
||||
}
|
||||
|
||||
let row = {id: Setting.getRandomName(), startTime: currentTime, endTime: Setting.toFixed(currentTime + 1, 3), text: ""};
|
||||
const row = {id: Setting.getRandomName(), startTime: currentTime, endTime: Setting.toFixed(currentTime + 1, 3), text: ""};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
|
@ -74,9 +74,9 @@ class LabelTable extends React.Component {
|
|||
}
|
||||
|
||||
downloadLabels(table) {
|
||||
let data = [];
|
||||
const data = [];
|
||||
table.forEach((label, i) => {
|
||||
let row = {};
|
||||
const row = {};
|
||||
|
||||
row[0] = label.startTime;
|
||||
row[1] = label.endTime;
|
||||
|
@ -84,7 +84,7 @@ class LabelTable extends React.Component {
|
|||
data.push(row);
|
||||
});
|
||||
|
||||
let sheet = XLSX.utils.json_to_sheet(data, {skipHeader: true});
|
||||
const sheet = XLSX.utils.json_to_sheet(data, {skipHeader: true});
|
||||
const blob = Setting.sheet2blob(sheet, "labels");
|
||||
const fileName = `labels-${this.props.video.name}-${table.length}.xlsx`;
|
||||
FileSaver.saveAs(blob, fileName);
|
||||
|
@ -94,9 +94,9 @@ class LabelTable extends React.Component {
|
|||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:No."),
|
||||
dataIndex: 'no',
|
||||
key: 'no',
|
||||
width: '70px',
|
||||
dataIndex: "no",
|
||||
key: "no",
|
||||
width: "70px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Button type={"text"} style={{width: "50px"}} onClick={() => {
|
||||
|
@ -106,72 +106,72 @@ class LabelTable extends React.Component {
|
|||
}} >
|
||||
{index + 1}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Start time (s)"),
|
||||
dataIndex: 'startTime',
|
||||
key: 'startTime',
|
||||
width: '120px',
|
||||
dataIndex: "startTime",
|
||||
key: "startTime",
|
||||
width: "120px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<InputNumber style={{width: "100%"}} min={0} value={text} onChange={value => {
|
||||
this.updateField(table, index, 'startTime', value);
|
||||
this.updateField(table, index, "startTime", value);
|
||||
if (record.endTime <= value) {
|
||||
this.updateField(table, index, 'endTime', Setting.toFixed(value + 1, 3));
|
||||
this.updateField(table, index, "endTime", Setting.toFixed(value + 1, 3));
|
||||
}
|
||||
this.reorderTable(table);
|
||||
}} />
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:End time (s)"),
|
||||
dataIndex: 'endTime',
|
||||
key: 'endTime',
|
||||
width: '120px',
|
||||
dataIndex: "endTime",
|
||||
key: "endTime",
|
||||
width: "120px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<InputNumber style={{width: "100%"}} min={record.startTime} value={text} onChange={value => {
|
||||
this.updateField(table, index, 'endTime', value);
|
||||
this.updateField(table, index, "endTime", value);
|
||||
this.reorderTable(table);
|
||||
}} />
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Text"),
|
||||
dataIndex: 'text',
|
||||
key: 'text',
|
||||
dataIndex: "text",
|
||||
key: "text",
|
||||
// width: '200px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, 'text', e.target.value);
|
||||
this.updateField(table, index, "text", e.target.value);
|
||||
}} />
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
key: 'action',
|
||||
width: '50px',
|
||||
key: "action",
|
||||
width: "50px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
{/*<Tooltip placement="bottomLeft" title={"Up"}>*/}
|
||||
{/* <Tooltip placement="bottomLeft" title={"Up"}>*/}
|
||||
{/* <Button style={{marginRight: "5px"}} disabled={index === 0} icon={<UpOutlined />} size="small" onClick={() => this.upRow(table, index)} />*/}
|
||||
{/*</Tooltip>*/}
|
||||
{/*<Tooltip placement="topLeft" title={"Down"}>*/}
|
||||
{/* </Tooltip>*/}
|
||||
{/* <Tooltip placement="topLeft" title={"Down"}>*/}
|
||||
{/* <Button style={{marginRight: "5px"}} disabled={index === table.length - 1} icon={<DownOutlined />} size="small" onClick={() => this.downRow(table, index)} />*/}
|
||||
{/*</Tooltip>*/}
|
||||
{/* </Tooltip>*/}
|
||||
<Tooltip placement="right" title={"Delete"}>
|
||||
<Button icon={<DeleteOutlined />} size="small" onClick={() => this.deleteRow(table, index)} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -209,7 +209,7 @@ class LabelTable extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col span={24}>
|
||||
{
|
||||
this.renderTable(this.props.table)
|
||||
|
@ -217,7 +217,7 @@ class LabelTable extends React.Component {
|
|||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {message, Tag, Tooltip} from "antd";
|
||||
import {Tag, Tooltip, message} from "antd";
|
||||
import {SyncOutlined} from "@ant-design/icons";
|
||||
import {isMobile as isMobileDevice} from "react-device-detect";
|
||||
import i18next from "i18next";
|
||||
|
@ -8,12 +8,12 @@ import XLSX from "xlsx";
|
|||
import moment from "moment/moment";
|
||||
import * as StoreBackend from "./backend/StoreBackend";
|
||||
|
||||
export let ServerUrl = '';
|
||||
export let ServerUrl = "";
|
||||
export let CasdoorSdk;
|
||||
|
||||
export function initServerUrl() {
|
||||
const hostname = window.location.hostname;
|
||||
if (hostname === 'localhost') {
|
||||
if (hostname === "localhost") {
|
||||
ServerUrl = `http://${hostname}:14000`;
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ export function myParseInt(i) {
|
|||
|
||||
export function openLink(link) {
|
||||
// this.props.history.push(link);
|
||||
const w = window.open('about:blank');
|
||||
const w = window.open("about:blank");
|
||||
w.location.href = link;
|
||||
}
|
||||
|
||||
|
@ -134,11 +134,9 @@ export function trim(str, ch) {
|
|||
let start = 0;
|
||||
let end = str.length;
|
||||
|
||||
while(start < end && str[start] === ch)
|
||||
++start;
|
||||
while (start < end && str[start] === ch) {++start;}
|
||||
|
||||
while(end > start && str[end - 1] === ch)
|
||||
--end;
|
||||
while (end > start && str[end - 1] === ch) {--end;}
|
||||
|
||||
return (start > 0 || end < str.length) ? str.substring(start, end) : str;
|
||||
}
|
||||
|
@ -153,8 +151,8 @@ export function getFormattedDate(date) {
|
|||
return null;
|
||||
}
|
||||
|
||||
date = date.replace('T', ' ');
|
||||
date = date.replace('+08:00', ' ');
|
||||
date = date.replace("T", " ");
|
||||
date = date.replace("+08:00", " ");
|
||||
return date;
|
||||
}
|
||||
|
||||
|
@ -163,10 +161,10 @@ export function getFormattedDateShort(date) {
|
|||
}
|
||||
|
||||
export function getShortName(s) {
|
||||
return s.split('/').slice(-1)[0];
|
||||
return s.split("/").slice(-1)[0];
|
||||
}
|
||||
|
||||
export function getShortText(s, maxLength=35) {
|
||||
export function getShortText(s, maxLength = 35) {
|
||||
if (s.length > maxLength) {
|
||||
return `${s.slice(0, maxLength)}...`;
|
||||
} else {
|
||||
|
@ -178,7 +176,7 @@ function getRandomInt(s) {
|
|||
let hash = 0;
|
||||
if (s.length !== 0) {
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
let char = s.charCodeAt(i);
|
||||
const char = s.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash;
|
||||
}
|
||||
|
@ -188,7 +186,7 @@ function getRandomInt(s) {
|
|||
}
|
||||
|
||||
export function getAvatarColor(s) {
|
||||
const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
|
||||
const colorList = ["#f56a00", "#7265e6", "#ffbf00", "#00a2ae"];
|
||||
let random = getRandomInt(s);
|
||||
if (random < 0) {
|
||||
random = -random;
|
||||
|
@ -241,7 +239,7 @@ export function getTag(text, type, state) {
|
|||
let icon = null;
|
||||
let style = {};
|
||||
if (state === "Pending") {
|
||||
icon = <SyncOutlined spin />
|
||||
icon = <SyncOutlined spin />;
|
||||
style = {borderStyle: "dashed", backgroundColor: "white"};
|
||||
}
|
||||
|
||||
|
@ -252,7 +250,7 @@ export function getTag(text, type, state) {
|
|||
{text}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
} else if (type === "Write") {
|
||||
return (
|
||||
<Tooltip placement="top" title={"Write"}>
|
||||
|
@ -260,7 +258,7 @@ export function getTag(text, type, state) {
|
|||
{text}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
} else if (type === "Admin") {
|
||||
return (
|
||||
<Tooltip placement="top" title={"Admin"}>
|
||||
|
@ -268,7 +266,7 @@ export function getTag(text, type, state) {
|
|||
{text}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -279,7 +277,7 @@ export function getTags(vectors) {
|
|||
return [];
|
||||
}
|
||||
|
||||
let res = [];
|
||||
const res = [];
|
||||
vectors.forEach((vector, i) => {
|
||||
if (vector.data.length !== 0) {
|
||||
res.push(
|
||||
|
@ -305,7 +303,7 @@ export function getLabelTags(labels) {
|
|||
return [];
|
||||
}
|
||||
|
||||
let res = [];
|
||||
const res = [];
|
||||
labels.forEach((label, i) => {
|
||||
res.push(
|
||||
<Tooltip placement="top" title={getShortText(JSON.stringify(label.text), 500)}>
|
||||
|
@ -329,7 +327,7 @@ export function getPercentage(f) {
|
|||
function s2ab(s) {
|
||||
const buf = new ArrayBuffer(s.length);
|
||||
const view = new Uint8Array(buf);
|
||||
for (let i = 0; i !== s.length; i ++) {
|
||||
for (let i = 0; i !== s.length; i++) {
|
||||
view[i] = s.charCodeAt(i) & 0xFF;
|
||||
}
|
||||
return buf;
|
||||
|
@ -355,9 +353,9 @@ export function workbook2blob(workbook) {
|
|||
}
|
||||
|
||||
export function downloadXlsx(wordset) {
|
||||
let data = [];
|
||||
const data = [];
|
||||
wordset.vectors.forEach((vector, i) => {
|
||||
let row = {};
|
||||
const row = {};
|
||||
|
||||
row[0] = vector.name;
|
||||
vector.data.forEach((dataItem, i) => {
|
||||
|
@ -367,7 +365,7 @@ export function downloadXlsx(wordset) {
|
|||
data.push(row);
|
||||
});
|
||||
|
||||
let sheet = XLSX.utils.json_to_sheet(data, {skipHeader: true});
|
||||
const sheet = XLSX.utils.json_to_sheet(data, {skipHeader: true});
|
||||
// sheet["!cols"] = [
|
||||
// { wch: 18 },
|
||||
// { wch: 7 },
|
||||
|
@ -391,11 +389,11 @@ export function getFriendlyFileSize(size) {
|
|||
return size + " B";
|
||||
}
|
||||
|
||||
let i = Math.floor(Math.log(size) / Math.log(1024));
|
||||
const i = Math.floor(Math.log(size) / Math.log(1024));
|
||||
let num = (size / Math.pow(1024, i));
|
||||
let round = Math.round(num);
|
||||
const round = Math.round(num);
|
||||
num = round < 10 ? num.toFixed(2) : round < 100 ? num.toFixed(1) : round;
|
||||
return `${num} ${"KMGTPEZY"[i-1]}B`;
|
||||
return `${num} ${"KMGTPEZY"[i - 1]}B`;
|
||||
}
|
||||
|
||||
export function getTreeWithParents(tree) {
|
||||
|
@ -437,14 +435,14 @@ export function getTreeWithSearch(tree, s) {
|
|||
export function getExtFromPath(path) {
|
||||
const filename = path.split("/").pop();
|
||||
if (filename.includes(".")) {
|
||||
return filename.split('.').pop().toLowerCase();
|
||||
return filename.split(".").pop().toLowerCase();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export function getExtFromFile(file) {
|
||||
const res = file.title.split('.')[1];
|
||||
const res = file.title.split(".")[1];
|
||||
if (res === undefined) {
|
||||
return "";
|
||||
} else {
|
||||
|
@ -472,7 +470,7 @@ export function getCollectedTime(filename) {
|
|||
}
|
||||
|
||||
const time = tokens[0].slice(0, -3);
|
||||
const m = new moment(time, 'YYYYMMDD_HH:mm:ss');
|
||||
const m = new moment(time, "YYYYMMDD_HH:mm:ss");
|
||||
return m.format();
|
||||
}
|
||||
|
||||
|
@ -496,14 +494,14 @@ export function getSubject(filename) {
|
|||
}
|
||||
|
||||
export function submitStoreEdit(storeObj) {
|
||||
let store = deepCopy(storeObj);
|
||||
const store = deepCopy(storeObj);
|
||||
store.fileTree = undefined;
|
||||
StoreBackend.updateStore(storeObj.owner, storeObj.name, store)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
showMessage("success", `Successfully saved`);
|
||||
showMessage("success", "Successfully saved");
|
||||
} else {
|
||||
showMessage("error", `failed to save: server side failure`);
|
||||
showMessage("error", "failed to save: server side failure");
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react';
|
||||
import React from "react";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class SigninPage extends React.Component {
|
||||
componentDidMount(){
|
||||
componentDidMount() {
|
||||
window.location.replace(Setting.getSigninUrl());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row} from 'antd';
|
||||
import {Button, Card, Col, Input, Row} from "antd";
|
||||
import {LinkOutlined} from "@ant-design/icons";
|
||||
import * as StoreBackend from "./backend/StoreBackend";
|
||||
import * as Setting from "./Setting";
|
||||
|
@ -40,7 +40,7 @@ class StoreEditPage extends React.Component {
|
|||
updateStoreField(key, value) {
|
||||
value = this.parseStoreField(key, value);
|
||||
|
||||
let store = this.state.store;
|
||||
const store = this.state.store;
|
||||
store[key] = value;
|
||||
this.setState({
|
||||
store: store,
|
||||
|
@ -54,49 +54,49 @@ class StoreEditPage extends React.Component {
|
|||
{i18next.t("store:Edit Store")}
|
||||
<Button type="primary" onClick={this.submitStoreEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
} style={{marginLeft: "5px"}} type="inner">
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.store.name} onChange={e => {
|
||||
this.updateStoreField('name', e.target.value);
|
||||
this.updateStoreField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Display name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.store.displayName} onChange={e => {
|
||||
this.updateStoreField('displayName', e.target.value);
|
||||
this.updateStoreField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("store:Bucket")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.store.bucket} onChange={e => {
|
||||
this.updateStoreField('bucket', e.target.value);
|
||||
this.updateStoreField("bucket", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("store:Domain")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input prefix={<LinkOutlined/>} value={this.state.store.domain} onChange={e => {
|
||||
this.updateStoreField('domain', e.target.value);
|
||||
<Input prefix={<LinkOutlined />} value={this.state.store.domain} onChange={e => {
|
||||
this.updateStoreField("domain", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("store:File tree")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
|
@ -109,23 +109,23 @@ class StoreEditPage extends React.Component {
|
|||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
submitStoreEdit() {
|
||||
let store = Setting.deepCopy(this.state.store);
|
||||
const store = Setting.deepCopy(this.state.store);
|
||||
store.fileTree = undefined;
|
||||
StoreBackend.updateStore(this.state.store.owner, this.state.storeName, store)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Setting.showMessage("success", `Successfully saved`);
|
||||
Setting.showMessage("success", "Successfully saved");
|
||||
this.setState({
|
||||
storeName: this.state.store.name,
|
||||
});
|
||||
this.props.history.push(`/stores/${this.state.store.owner}/${this.state.store.name}`);
|
||||
} else {
|
||||
Setting.showMessage("error", `failed to save: server side failure`);
|
||||
this.updateStoreField('name', this.state.storeName);
|
||||
Setting.showMessage("error", "failed to save: server side failure");
|
||||
this.updateStoreField("name", this.state.storeName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
||||
import {Button, Col, Popconfirm, Row, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as StoreBackend from "./backend/StoreBackend";
|
||||
|
@ -37,14 +37,14 @@ class StoreListPage extends React.Component {
|
|||
bucket: "bucket_name",
|
||||
domain: "https://cdn.example.com",
|
||||
propertiesMap: {},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
addStore() {
|
||||
const newStore = this.newStore();
|
||||
StoreBackend.addStore(newStore)
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Store added successfully`);
|
||||
Setting.showMessage("success", "Store added successfully");
|
||||
this.setState({
|
||||
stores: Setting.prependRow(this.state.stores, newStore),
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ class StoreListPage extends React.Component {
|
|||
deleteStore(i) {
|
||||
StoreBackend.deleteStore(this.state.stores[i])
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Store deleted successfully`);
|
||||
Setting.showMessage("success", "Store deleted successfully");
|
||||
this.setState({
|
||||
stores: Setting.deleteRow(this.state.stores, i),
|
||||
});
|
||||
|
@ -71,52 +71,52 @@ class StoreListPage extends React.Component {
|
|||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '300px',
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "300px",
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/stores/${record.owner}/${text}/view`}>
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
// width: '200px',
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '240px',
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: "240px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} onClick={() => this.props.history.push(`/stores/${record.owner}/${record.name}/view`)}>{i18next.t("general:View")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/stores/${record.owner}/${record.name}/view`)}>{i18next.t("general:View")}</Button>
|
||||
{
|
||||
!Setting.isLocalAdminUser(this.props.account) ? null : (
|
||||
<React.Fragment>
|
||||
<Button style={{marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/stores/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Button style={{marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/stores/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete store: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteStore(index)}
|
||||
okText="OK"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {DownOutlined, DeleteOutlined, UpOutlined} from '@ant-design/icons';
|
||||
import {Button, Col, Input, Row, Table, Tooltip} from 'antd';
|
||||
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
|
||||
import {Button, Col, Input, Row, Table, Tooltip} from "antd";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
|
@ -36,7 +36,7 @@ class VectorTable extends React.Component {
|
|||
}
|
||||
|
||||
addRow(table) {
|
||||
let row = {no: table.length, name: `New Vector - ${table.length}`, data: []};
|
||||
const row = {no: table.length, name: `New Vector - ${table.length}`, data: []};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
|
@ -63,43 +63,43 @@ class VectorTable extends React.Component {
|
|||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:No."),
|
||||
dataIndex: 'no',
|
||||
key: 'no',
|
||||
width: '60px',
|
||||
dataIndex: "no",
|
||||
key: "no",
|
||||
width: "60px",
|
||||
render: (text, record, index) => {
|
||||
return (index + 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '200px',
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "200px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, 'name', e.target.value);
|
||||
this.updateField(table, index, "name", e.target.value);
|
||||
}} />
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Data"),
|
||||
dataIndex: 'data',
|
||||
key: 'data',
|
||||
dataIndex: "data",
|
||||
key: "data",
|
||||
// width: '300px',
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, 'data', e.target.value);
|
||||
this.updateField(table, index, "data", e.target.value);
|
||||
}} />
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
key: 'action',
|
||||
width: '100px',
|
||||
key: "action",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
|
@ -114,7 +114,7 @@ class VectorTable extends React.Component {
|
|||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -138,7 +138,7 @@ class VectorTable extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col span={24}>
|
||||
{
|
||||
this.renderTable(this.props.table)
|
||||
|
@ -146,7 +146,7 @@ class VectorTable extends React.Component {
|
|||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {Button, Card, Col, Input, InputNumber, Row} from 'antd';
|
||||
import {Button, Card, Col, Input, InputNumber, Row} from "antd";
|
||||
import * as VectorsetBackend from "./backend/VectorsetBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
@ -39,7 +39,7 @@ class VectorsetEditPage extends React.Component {
|
|||
updateVectorsetField(key, value) {
|
||||
value = this.parseVectorsetField(key, value);
|
||||
|
||||
let vectorset = this.state.vectorset;
|
||||
const vectorset = this.state.vectorset;
|
||||
vectorset[key] = value;
|
||||
this.setState({
|
||||
vectorset: vectorset,
|
||||
|
@ -53,106 +53,106 @@ class VectorsetEditPage extends React.Component {
|
|||
{i18next.t("vectorset:Edit Vectorset")}
|
||||
<Button type="primary" onClick={this.submitVectorsetEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
} style={{marginLeft: "5px"}} type="inner">
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.vectorset.name} onChange={e => {
|
||||
this.updateVectorsetField('name', e.target.value);
|
||||
this.updateVectorsetField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Display name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.vectorset.displayName} onChange={e => {
|
||||
this.updateVectorsetField('displayName', e.target.value);
|
||||
this.updateVectorsetField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:URL")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input prefix={<LinkOutlined/>} value={this.state.vectorset.url} onChange={e => {
|
||||
this.updateVectorsetField('url', e.target.value);
|
||||
<Input prefix={<LinkOutlined />} value={this.state.vectorset.url} onChange={e => {
|
||||
this.updateVectorsetField("url", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("vectorset:File name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.vectorset.fileName} onChange={e => {
|
||||
this.updateVectorsetField('fileName', e.target.value);
|
||||
this.updateVectorsetField("fileName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("vectorset:File size")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.vectorset.fileSize} onChange={e => {
|
||||
this.updateVectorsetField('fileSize', e.target.value);
|
||||
this.updateVectorsetField("fileSize", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("vectorset:Dimension")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber value={this.state.vectorset.dimension} onChange={value => {
|
||||
this.updateVectorsetField('dimension', value);
|
||||
this.updateVectorsetField("dimension", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("vectorset:Count")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber value={this.state.vectorset.count} onChange={value => {
|
||||
this.updateVectorsetField('count', value);
|
||||
this.updateVectorsetField("count", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("vectorset:Example vectors")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<VectorTable
|
||||
title={i18next.t("vectorset:Example vectors")}
|
||||
table={this.state.vectorset.vectors}
|
||||
onUpdateTable={(value) => { this.updateVectorsetField('vectors', value)}}
|
||||
onUpdateTable={(value) => {this.updateVectorsetField("vectors", value);}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
submitVectorsetEdit() {
|
||||
let vectorset = Setting.deepCopy(this.state.vectorset);
|
||||
const vectorset = Setting.deepCopy(this.state.vectorset);
|
||||
VectorsetBackend.updateVectorset(this.state.vectorset.owner, this.state.vectorsetName, vectorset)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Setting.showMessage("success", `Successfully saved`);
|
||||
Setting.showMessage("success", "Successfully saved");
|
||||
this.setState({
|
||||
vectorsetName: this.state.vectorset.name,
|
||||
});
|
||||
this.props.history.push(`/vectorsets/${this.state.vectorset.name}`);
|
||||
} else {
|
||||
Setting.showMessage("error", `failed to save: server side failure`);
|
||||
this.updateVectorsetField('name', this.state.vectorsetName);
|
||||
Setting.showMessage("error", "failed to save: server side failure");
|
||||
this.updateVectorsetField("name", this.state.vectorsetName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
||||
import {Button, Col, Popconfirm, Row, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as VectorsetBackend from "./backend/VectorsetBackend";
|
||||
|
@ -40,14 +40,14 @@ class VectorsetListPage extends React.Component {
|
|||
dimension: 128,
|
||||
count: 10000,
|
||||
vectors: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
addVectorset() {
|
||||
const newVectorset = this.newVectorset();
|
||||
VectorsetBackend.addVectorset(newVectorset)
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Vectorset added successfully`);
|
||||
Setting.showMessage("success", "Vectorset added successfully");
|
||||
this.setState({
|
||||
vectorsets: Setting.prependRow(this.state.vectorsets, newVectorset),
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ class VectorsetListPage extends React.Component {
|
|||
deleteVectorset(i) {
|
||||
VectorsetBackend.deleteVectorset(this.state.vectorsets[i])
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Vectorset deleted successfully`);
|
||||
Setting.showMessage("success", "Vectorset deleted successfully");
|
||||
this.setState({
|
||||
vectorsets: Setting.deleteRow(this.state.vectorsets, i),
|
||||
});
|
||||
|
@ -74,30 +74,30 @@ class VectorsetListPage extends React.Component {
|
|||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '140px',
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "140px",
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/vectorsets/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
width: '200px',
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
width: "200px",
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:URL"),
|
||||
dataIndex: 'url',
|
||||
key: 'url',
|
||||
width: '250px',
|
||||
dataIndex: "url",
|
||||
key: "url",
|
||||
width: "250px",
|
||||
sorter: (a, b) => a.url.localeCompare(b.url),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
|
@ -106,67 +106,67 @@ class VectorsetListPage extends React.Component {
|
|||
Setting.getShortText(text)
|
||||
}
|
||||
</a>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("vectorset:File name"),
|
||||
dataIndex: 'fileName',
|
||||
key: 'fileName',
|
||||
width: '200px',
|
||||
dataIndex: "fileName",
|
||||
key: "fileName",
|
||||
width: "200px",
|
||||
sorter: (a, b) => a.fileName.localeCompare(b.fileName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("vectorset:File size"),
|
||||
dataIndex: 'fileSize',
|
||||
key: 'fileSize',
|
||||
width: '120px',
|
||||
dataIndex: "fileSize",
|
||||
key: "fileSize",
|
||||
width: "120px",
|
||||
sorter: (a, b) => a.fileSize.localeCompare(b.fileSize),
|
||||
},
|
||||
{
|
||||
title: i18next.t("vectorset:Dimension"),
|
||||
dataIndex: 'dimension',
|
||||
key: 'dimension',
|
||||
width: '110px',
|
||||
dataIndex: "dimension",
|
||||
key: "dimension",
|
||||
width: "110px",
|
||||
sorter: (a, b) => a.dimension - b.dimension,
|
||||
},
|
||||
{
|
||||
title: i18next.t("vectorset:Example vectors"),
|
||||
dataIndex: 'vectors',
|
||||
key: 'vectors',
|
||||
dataIndex: "vectors",
|
||||
key: "vectors",
|
||||
// width: '120px',
|
||||
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("vectorset:Count"),
|
||||
dataIndex: 'count',
|
||||
key: 'count',
|
||||
width: '110px',
|
||||
dataIndex: "count",
|
||||
key: "count",
|
||||
width: "110px",
|
||||
sorter: (a, b) => a.count - b.count,
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '80px',
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: "80px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/vectorsets/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/vectorsets/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete vectorset: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteVectorset(index)}
|
||||
okText="OK"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import Player from 'aliplayer-react';
|
||||
import Player from "aliplayer-react";
|
||||
import * as Setting from "./Setting";
|
||||
import BulletScreen from 'rc-bullets';
|
||||
import BulletScreen from "rc-bullets";
|
||||
|
||||
class Video extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -62,8 +62,8 @@ class Video extends React.Component {
|
|||
backgroundColor: "rgb(255,255,255)",
|
||||
}, {
|
||||
onStart: (bulletId, screen) => {
|
||||
let bulletIdTextMap = this.state.bulletIdTextMap;
|
||||
let bulletTextMap = this.state.bulletTextMap;
|
||||
const bulletIdTextMap = this.state.bulletIdTextMap;
|
||||
const bulletTextMap = this.state.bulletTextMap;
|
||||
|
||||
bulletIdTextMap[bulletId] = label.text;
|
||||
bulletTextMap[label.text] = 1;
|
||||
|
@ -76,8 +76,8 @@ class Video extends React.Component {
|
|||
// console.log(`start: ${bulletId}`);
|
||||
},
|
||||
onEnd: (bulletId, screen) => {
|
||||
let bulletIdTextMap = this.state.bulletIdTextMap;
|
||||
let bulletTextMap = this.state.bulletTextMap;
|
||||
const bulletIdTextMap = this.state.bulletIdTextMap;
|
||||
const bulletTextMap = this.state.bulletTextMap;
|
||||
|
||||
const text = bulletIdTextMap[bulletId];
|
||||
delete bulletIdTextMap[bulletId];
|
||||
|
@ -140,11 +140,11 @@ class Video extends React.Component {
|
|||
initPlayer(player) {
|
||||
// https://help.aliyun.com/document_detail/125572.html
|
||||
// https://github.com/zerosoul/rc-bullets
|
||||
player.on('ready', () => {this.handleReady(player)});
|
||||
player.on('timeupdate', () => {this.onTimeUpdate(player)});
|
||||
player.on('play', () => {this.onPlay()});
|
||||
player.on('pause', () => {this.onPause()});
|
||||
player.on('completeSeek', () => {this.state.screen.clear()});
|
||||
player.on("ready", () => {this.handleReady(player);});
|
||||
player.on("timeupdate", () => {this.onTimeUpdate(player);});
|
||||
player.on("play", () => {this.onPlay();});
|
||||
player.on("pause", () => {this.onPause();});
|
||||
player.on("completeSeek", () => {this.state.screen.clear();});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -186,7 +186,7 @@ class Video extends React.Component {
|
|||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { Component } from "react";
|
||||
import ReactEcharts from 'echarts-for-react';
|
||||
import React, {Component} from "react";
|
||||
import ReactEcharts from "echarts-for-react";
|
||||
|
||||
class VideoDataChart extends Component {
|
||||
drawPic(data, currentTime, height) {
|
||||
|
@ -24,35 +24,35 @@ class VideoDataChart extends Component {
|
|||
|
||||
const option = {
|
||||
grid: {
|
||||
top: '5%',
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '8%',
|
||||
containLabel: true
|
||||
top: "5%",
|
||||
left: "3%",
|
||||
right: "4%",
|
||||
bottom: "8%",
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xAxisData
|
||||
type: "category",
|
||||
data: xAxisData,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
type: "value",
|
||||
min: dataMin,
|
||||
max: dataMax
|
||||
max: dataMax,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Data',
|
||||
type: 'line',
|
||||
name: "Data",
|
||||
type: "line",
|
||||
data: seriesData,
|
||||
markLine: {
|
||||
data: [
|
||||
[
|
||||
{
|
||||
symbol: 'none',
|
||||
symbol: "none",
|
||||
x: `${currentTime / 0.65 + 40}`,
|
||||
y: '80%',
|
||||
y: "80%",
|
||||
lineStyle: {
|
||||
color: 'red'
|
||||
color: "red",
|
||||
},
|
||||
// label: {
|
||||
// position: 'start',
|
||||
|
@ -60,19 +60,19 @@ class VideoDataChart extends Component {
|
|||
// },
|
||||
},
|
||||
{
|
||||
symbol: 'none',
|
||||
symbol: "none",
|
||||
x: `${currentTime / 0.65 + 40}`,
|
||||
y: '0%',
|
||||
y: "0%",
|
||||
// label: {
|
||||
// position: 'start',
|
||||
// formatter: 'Max'
|
||||
// },
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {Affix, Button, Card, Col, Input, Row, Select, Switch} from 'antd';
|
||||
import {Affix, Button, Card, Col, Input, Row, Select, Switch} from "antd";
|
||||
import * as VideoBackend from "./backend/VideoBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
@ -9,7 +9,7 @@ import LabelTable from "./LabelTable";
|
|||
import * as Papa from "papaparse";
|
||||
import VideoDataChart from "./VideoDataChart";
|
||||
|
||||
const { Option } = Select;
|
||||
const {Option} = Select;
|
||||
|
||||
class VideoEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -55,7 +55,7 @@ class VideoEditPage extends React.Component {
|
|||
updateVideoField(key, value) {
|
||||
value = this.parseVideoField(key, value);
|
||||
|
||||
let video = this.state.video;
|
||||
const video = this.state.video;
|
||||
video[key] = value;
|
||||
this.setState({
|
||||
video: video,
|
||||
|
@ -73,7 +73,7 @@ class VideoEditPage extends React.Component {
|
|||
return null;
|
||||
}
|
||||
|
||||
let task = {};
|
||||
const task = {};
|
||||
task.video = {
|
||||
vid: this.state.video.videoId,
|
||||
playAuth: this.state.video.playAuth,
|
||||
|
@ -98,16 +98,16 @@ class VideoEditPage extends React.Component {
|
|||
this.state.currentTime
|
||||
}
|
||||
</div>
|
||||
<div className="screen" style={{position: "absolute", zIndex: 100, pointerEvents: "none", width: '440px', height: '472px', marginLeft: '200px', marginRight: '200px', backgroundColor: "rgba(255,0,0,0)" }}></div>
|
||||
<div className="screen" style={{position: "absolute", zIndex: 100, pointerEvents: "none", width: "440px", height: "472px", marginLeft: "200px", marginRight: "200px", backgroundColor: "rgba(255,0,0,0)"}}></div>
|
||||
<Video task={task} labels={this.state.video.labels}
|
||||
onUpdateTime={(time) => {this.setState({currentTime: time})}}
|
||||
onCreatePlayer={(player) => {this.setState({player: player})}}
|
||||
onCreateScreen={(screen) => {this.setState({screen: screen})}}
|
||||
onCreateVideo={(videoObj) => {this.setState({videoObj: videoObj})}}
|
||||
onPause={() => {this.onPause()}}
|
||||
onUpdateTime={(time) => {this.setState({currentTime: time});}}
|
||||
onCreatePlayer={(player) => {this.setState({player: player});}}
|
||||
onCreateScreen={(screen) => {this.setState({screen: screen});}}
|
||||
onCreateVideo={(videoObj) => {this.setState({videoObj: videoObj});}}
|
||||
onPause={() => {this.onPause();}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getDataAndParse(dataUrl) {
|
||||
|
@ -115,11 +115,11 @@ class VideoEditPage extends React.Component {
|
|||
method: "GET",
|
||||
}).then(res => res.text())
|
||||
.then(res => {
|
||||
const result = Papa.parse(res, { header: true });
|
||||
const result = Papa.parse(res, {header: true});
|
||||
let data = result.data;
|
||||
data = data.filter(item => item.time !== "");
|
||||
data = data.map(item => {
|
||||
let res = {};
|
||||
const res = {};
|
||||
res.time = Number(item.time);
|
||||
res.data = Number(item.data);
|
||||
return res;
|
||||
|
@ -137,76 +137,76 @@ class VideoEditPage extends React.Component {
|
|||
{i18next.t("video:Edit Video")}
|
||||
<Button type="primary" onClick={this.submitVideoEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
} style={{marginLeft: "5px"}} type="inner">
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.video.name} onChange={e => {
|
||||
this.updateVideoField('name', e.target.value);
|
||||
this.updateVideoField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Display name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.video.displayName} onChange={e => {
|
||||
this.updateVideoField('displayName', e.target.value);
|
||||
this.updateVideoField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Video ID")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.video.videoId} onChange={e => {
|
||||
this.updateVideoField('videoId', e.target.value);
|
||||
this.updateVideoField("videoId", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Cover")}:
|
||||
</Col>
|
||||
<Col span={22} style={(Setting.isMobile()) ? {maxWidth:'100%'} :{}}>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||
<Col span={22} style={(Setting.isMobile()) ? {maxWidth: "100%"} : {}}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||
{i18next.t("general:URL")} :
|
||||
</Col>
|
||||
<Col span={23} >
|
||||
<Input disabled={true} prefix={<LinkOutlined/>} value={this.state.video.coverUrl} onChange={e => {
|
||||
this.updateVideoField('coverUrl', e.target.value);
|
||||
<Input disabled={true} prefix={<LinkOutlined />} value={this.state.video.coverUrl} onChange={e => {
|
||||
this.updateVideoField("coverUrl", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
|
||||
{i18next.t("general:Preview")}:
|
||||
</Col>
|
||||
<Col span={23} >
|
||||
<a target="_blank" rel="noreferrer" href={this.state.video.coverUrl}>
|
||||
<img src={this.state.video.coverUrl} alt={this.state.video.coverUrl} height={90} style={{marginBottom: '20px'}}/>
|
||||
<img src={this.state.video.coverUrl} alt={this.state.video.coverUrl} height={90} style={{marginBottom: "20px"}} />
|
||||
</a>
|
||||
</Col>
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Tag on pause")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Switch checked={this.state.video.tagOnPause} onChange={checked => {
|
||||
this.updateVideoField('tagOnPause', checked);
|
||||
this.updateVideoField("tagOnPause", checked);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("video:Video")}:
|
||||
</Col>
|
||||
<Col span={11} style={(Setting.isMobile()) ? {maxWidth: "100%"} : {}}>
|
||||
|
@ -215,14 +215,14 @@ class VideoEditPage extends React.Component {
|
|||
{
|
||||
this.state.video !== null ? this.renderVideoContent() : null
|
||||
}
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Data")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: '100%', marginBottom: "10px"}} value={this.state.video.dataUrl} onChange={(value => {
|
||||
<Select virtual={false} style={{width: "100%", marginBottom: "10px"}} value={this.state.video.dataUrl} onChange={(value => {
|
||||
this.getDataAndParse(value);
|
||||
this.updateVideoField('dataUrl', value);
|
||||
this.updateVideoField("dataUrl", value);
|
||||
})}>
|
||||
{
|
||||
this.state.video.dataUrls?.map((dataUrl, index) => <Option key={index} value={dataUrl}>{dataUrl.split("/").pop()}</Option>)
|
||||
|
@ -253,27 +253,27 @@ class VideoEditPage extends React.Component {
|
|||
player={this.state.player}
|
||||
screen={this.state.screen}
|
||||
videoObj={this.state.videoObj}
|
||||
onUpdateTable={(value) => {this.updateVideoField('labels', value)}}
|
||||
onUpdateTable={(value) => {this.updateVideoField("labels", value);}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
submitVideoEdit() {
|
||||
let video = Setting.deepCopy(this.state.video);
|
||||
const video = Setting.deepCopy(this.state.video);
|
||||
VideoBackend.updateVideo(this.state.video.owner, this.state.videoName, video)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Setting.showMessage("success", `Successfully saved`);
|
||||
Setting.showMessage("success", "Successfully saved");
|
||||
this.setState({
|
||||
videoName: this.state.video.name,
|
||||
});
|
||||
this.props.history.push(`/videos/${this.state.video.name}`);
|
||||
} else {
|
||||
Setting.showMessage("error", `failed to save: server side failure`);
|
||||
this.updateVideoField('name', this.state.videoName);
|
||||
Setting.showMessage("error", "failed to save: server side failure");
|
||||
this.updateVideoField("name", this.state.videoName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table, Upload} from 'antd';
|
||||
import {Button, Col, Popconfirm, Row, Table, Upload} from "antd";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
|
@ -41,14 +41,14 @@ class VideoListPage extends React.Component {
|
|||
dataUrls: [],
|
||||
dataUrl: "",
|
||||
playAuth: "",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
addVideo() {
|
||||
const newVideo = this.newVideo();
|
||||
VideoBackend.addVideo(newVideo)
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Video added successfully`);
|
||||
Setting.showMessage("success", "Video added successfully");
|
||||
this.setState({
|
||||
videos: Setting.prependRow(this.state.videos, newVideo),
|
||||
});
|
||||
|
@ -61,7 +61,7 @@ class VideoListPage extends React.Component {
|
|||
deleteVideo(i) {
|
||||
VideoBackend.deleteVideo(this.state.videos[i])
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Video deleted successfully`);
|
||||
Setting.showMessage("success", "Video deleted successfully");
|
||||
this.setState({
|
||||
videos: Setting.deleteRow(this.state.videos, i),
|
||||
});
|
||||
|
@ -72,28 +72,28 @@ class VideoListPage extends React.Component {
|
|||
}
|
||||
|
||||
uploadFile(info) {
|
||||
const { status, response: res } = info.file;
|
||||
if (status !== 'uploading') {
|
||||
const {status, response: res} = info.file;
|
||||
if (status !== "uploading") {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (status === 'done') {
|
||||
if (res.status === 'ok') {
|
||||
Setting.showMessage("success", `上传视频成功`);
|
||||
if (status === "done") {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "上传视频成功");
|
||||
const videoName = res.data;
|
||||
this.props.history.push(`/videos/${videoName}`);
|
||||
} else {
|
||||
Setting.showMessage("error", `上传视频失败:${res.msg}`);
|
||||
}
|
||||
} else if (status === 'error') {
|
||||
Setting.showMessage("success", `上传视频失败`);
|
||||
} else if (status === "error") {
|
||||
Setting.showMessage("success", "上传视频失败");
|
||||
}
|
||||
}
|
||||
|
||||
renderUpload() {
|
||||
const props = {
|
||||
name: 'file',
|
||||
accept: '.mp4',
|
||||
method: 'post',
|
||||
name: "file",
|
||||
accept: ".mp4",
|
||||
method: "post",
|
||||
action: `${Setting.ServerUrl}/api/upload-video`,
|
||||
withCredentials: true,
|
||||
onChange: (info) => {
|
||||
|
@ -107,91 +107,91 @@ class VideoListPage extends React.Component {
|
|||
<UploadOutlined /> 上传视频(.mp4)
|
||||
</Button>
|
||||
</Upload>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderTable(videos) {
|
||||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '140px',
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "140px",
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/videos/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
width: '200px',
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
width: "200px",
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Video ID"),
|
||||
dataIndex: 'videoId',
|
||||
key: 'videoId',
|
||||
width: '250px',
|
||||
dataIndex: "videoId",
|
||||
key: "videoId",
|
||||
width: "250px",
|
||||
sorter: (a, b) => a.videoId.localeCompare(b.videoId),
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Cover"),
|
||||
dataIndex: 'coverUrl',
|
||||
key: 'coverUrl',
|
||||
width: '200px',
|
||||
dataIndex: "coverUrl",
|
||||
key: "coverUrl",
|
||||
width: "200px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<a target="_blank" rel="noreferrer" href={text}>
|
||||
<img src={text} alt={text} width={150} />
|
||||
</a>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Labels"),
|
||||
dataIndex: 'labels',
|
||||
key: 'labels',
|
||||
dataIndex: "labels",
|
||||
key: "labels",
|
||||
// width: '120px',
|
||||
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getLabelTags(text);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("video:Label count"),
|
||||
dataIndex: 'labelCount',
|
||||
key: 'labelCount',
|
||||
width: '110px',
|
||||
dataIndex: "labelCount",
|
||||
key: "labelCount",
|
||||
width: "110px",
|
||||
render: (text, record, index) => {
|
||||
return record.labels.length;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '80px',
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: "80px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/videos/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/videos/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete video: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteVideo(index)}
|
||||
okText="OK"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -201,8 +201,8 @@ class VideoListPage extends React.Component {
|
|||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Videos")}
|
||||
{/* */}
|
||||
{/*<Button type="primary" size="small" onClick={this.addVideo.bind(this)}>{i18next.t("general:Add")}</Button>*/}
|
||||
{/* */}
|
||||
{/* <Button type="primary" size="small" onClick={this.addVideo.bind(this)}>{i18next.t("general:Add")}</Button>*/}
|
||||
|
||||
{
|
||||
this.renderUpload()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select} from 'antd';
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
|
||||
import * as WordsetBackend from "./backend/WordsetBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
@ -7,7 +7,7 @@ import VectorTable from "./VectorTable";
|
|||
import WordsetGraph from "./WordsetGraph";
|
||||
import * as VectorsetBackend from "./backend/VectorsetBackend";
|
||||
|
||||
const { Option } = Select;
|
||||
const {Option} = Select;
|
||||
|
||||
class WordsetEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -54,7 +54,7 @@ class WordsetEditPage extends React.Component {
|
|||
updateWordsetField(key, value) {
|
||||
value = this.parseWordsetField(key, value);
|
||||
|
||||
let wordset = this.state.wordset;
|
||||
const wordset = this.state.wordset;
|
||||
wordset[key] = value;
|
||||
this.setState({
|
||||
wordset: wordset,
|
||||
|
@ -71,41 +71,41 @@ class WordsetEditPage extends React.Component {
|
|||
{i18next.t("wordset:Edit Wordset")}
|
||||
<Button type="primary" onClick={this.submitWordsetEdit.bind(this)}>{i18next.t("general:Save")}</Button>
|
||||
</div>
|
||||
} style={{marginLeft: '5px'}} type="inner">
|
||||
<Row style={{marginTop: '10px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
} style={{marginLeft: "5px"}} type="inner">
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.wordset.name} onChange={e => {
|
||||
this.updateWordsetField('name', e.target.value);
|
||||
this.updateWordsetField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Display name")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.wordset.displayName} onChange={e => {
|
||||
this.updateWordsetField('displayName', e.target.value);
|
||||
this.updateWordsetField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("wordset:Vectorset")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: '100%'}} value={this.state.wordset.vectorset} onChange={(value => {this.updateWordsetField('vectorset', value);})}>
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.wordset.vectorset} onChange={(value => {this.updateWordsetField("vectorset", value);})}>
|
||||
{
|
||||
this.state.vectorsets?.map((vectorset, index) => <Option key={index} value={vectorset.name}>{vectorset.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("wordset:Match")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
|
@ -123,26 +123,26 @@ class WordsetEditPage extends React.Component {
|
|||
}}>{i18next.t("wordset:Match")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("wordset:Matched")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={`${Setting.getPercentage(allWords === 0 ? 0 : validWords / allWords)}% (${validWords} / ${allWords})`} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("wordset:Distance limit")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber value={this.state.wordset.distanceLimit} onChange={value => {
|
||||
this.updateWordsetField('distanceLimit', value);
|
||||
this.updateWordsetField("distanceLimit", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("wordset:Words")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
|
@ -150,12 +150,12 @@ class WordsetEditPage extends React.Component {
|
|||
title={i18next.t("wordset:Words")}
|
||||
table={this.state.wordset.vectors}
|
||||
wordset={this.state.wordset}
|
||||
onUpdateTable={(value) => { this.updateWordsetField('vectors', value)}}
|
||||
onUpdateTable={(value) => {this.updateWordsetField("vectors", value);}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: '20px'}} >
|
||||
<Col style={{marginTop: '5px'}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{i18next.t("general:Preview")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
|
@ -163,22 +163,22 @@ class WordsetEditPage extends React.Component {
|
|||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
submitWordsetEdit() {
|
||||
let wordset = Setting.deepCopy(this.state.wordset);
|
||||
const wordset = Setting.deepCopy(this.state.wordset);
|
||||
WordsetBackend.updateWordset(this.state.wordset.owner, this.state.wordsetName, wordset)
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
Setting.showMessage("success", `Successfully saved`);
|
||||
Setting.showMessage("success", "Successfully saved");
|
||||
this.setState({
|
||||
wordsetName: this.state.wordset.name,
|
||||
});
|
||||
this.props.history.push(`/wordsets/${this.state.wordset.name}`);
|
||||
} else {
|
||||
Setting.showMessage("error", `failed to save: server side failure`);
|
||||
this.updateWordsetField('name', this.state.wordsetName);
|
||||
Setting.showMessage("error", "failed to save: server side failure");
|
||||
this.updateWordsetField("name", this.state.wordsetName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import {Button, Card, Col, Empty, InputNumber, List, Row, Select, Slider, Spin, Switch, Tooltip} from "antd";
|
||||
import ForceGraph2D from 'react-force-graph-2d';
|
||||
import ForceGraph3D from 'react-force-graph-3d';
|
||||
import ForceGraph2D from "react-force-graph-2d";
|
||||
import ForceGraph3D from "react-force-graph-3d";
|
||||
import * as d3 from "d3-force";
|
||||
import * as WordsetBackend from "./backend/WordsetBackend";
|
||||
import i18next from "i18next";
|
||||
|
@ -9,9 +9,9 @@ import FileSaver from "file-saver";
|
|||
import XLSX from "xlsx";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
const { Option } = Select;
|
||||
const {Option} = Select;
|
||||
|
||||
let fg = React.createRef();
|
||||
const fg = React.createRef();
|
||||
|
||||
class ForceGraph extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -25,11 +25,11 @@ class ForceGraph extends React.Component {
|
|||
if (!this.props.enable3D) {
|
||||
return (
|
||||
<ForceGraph2D ref={fg} {...this.props} />
|
||||
)
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<ForceGraph3D ref={fg} {...this.props} />
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ class WordsetGraph extends React.Component {
|
|||
selectedType: null,
|
||||
selectedId: null,
|
||||
selectedIds: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
|
@ -62,7 +62,7 @@ class WordsetGraph extends React.Component {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
fg.current?.d3Force('collision', d3.forceCollide(15));
|
||||
fg.current?.d3Force("collision", d3.forceCollide(15));
|
||||
}
|
||||
|
||||
getWordsetGraph() {
|
||||
|
@ -84,7 +84,7 @@ class WordsetGraph extends React.Component {
|
|||
downloadClusters() {
|
||||
let data = [];
|
||||
this.state.graph.nodes.forEach((node, i) => {
|
||||
let row = {};
|
||||
const row = {};
|
||||
|
||||
row[0] = node.id;
|
||||
row[1] = parseInt(node.tag) + 1;
|
||||
|
@ -93,7 +93,7 @@ class WordsetGraph extends React.Component {
|
|||
|
||||
data = data.sort((a, b) => {return a[1] - b[1];});
|
||||
|
||||
let sheet = XLSX.utils.json_to_sheet(data, {skipHeader: true});
|
||||
const sheet = XLSX.utils.json_to_sheet(data, {skipHeader: true});
|
||||
const blob = Setting.sheet2blob(sheet, "clusters");
|
||||
const fileName = `clusters-${this.state.wordsetName}-${this.state.graph.nodes.length}-${this.state.clusterNumber}.xlsx`;
|
||||
FileSaver.saveAs(blob, fileName);
|
||||
|
@ -117,11 +117,11 @@ class WordsetGraph extends React.Component {
|
|||
<List.Item>
|
||||
{`${(link.source !== node.id && link.source.id !== node.id) ? link.source.id : link.target.id} | ${link.tag}`}
|
||||
</List.Item>
|
||||
)
|
||||
);
|
||||
}}
|
||||
pagination={{pageSize: 20, size: "small", showLessItems: true, simple: true}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderNodeMenu(selectedIds) {
|
||||
|
@ -142,11 +142,11 @@ class WordsetGraph extends React.Component {
|
|||
{`${node.id} | ${node.weight}`}
|
||||
</List.Item>
|
||||
</Tooltip>
|
||||
)
|
||||
);
|
||||
}}
|
||||
pagination={{pageSize: 20, size: "small", showLessItems: true, simple: true}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderLeftToolbar() {
|
||||
|
@ -165,7 +165,7 @@ class WordsetGraph extends React.Component {
|
|||
|
||||
return (
|
||||
<div style={{width: "100%", position: "fixed", zIndex: 2, pointerEvents: "none"}}>
|
||||
<div style={{width: "200px", marginLeft: '20px', marginTop: '20px', pointerEvents: "auto"}}>
|
||||
<div style={{width: "200px", marginLeft: "20px", marginTop: "20px", pointerEvents: "auto"}}>
|
||||
<Card style={{marginTop: "20px"}} size="small" title={
|
||||
<div>
|
||||
分类:{categoryName}
|
||||
|
@ -177,25 +177,25 @@ class WordsetGraph extends React.Component {
|
|||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderRightToolbar() {
|
||||
return (
|
||||
<div style={{width: "100%", position: "fixed", zIndex: 2, pointerEvents: "none"}}>
|
||||
<div style={{width: "200px", float: "right", marginRight: '20px', marginTop: '20px', pointerEvents: "auto"}}>
|
||||
<div style={{width: "200px", float: "right", marginRight: "20px", marginTop: "20px", pointerEvents: "auto"}}>
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
图形选项
|
||||
</div>
|
||||
} type="inner">
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
保持静止:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<Switch checked={this.state.enableStatic} onChange={(checked, e) => {
|
||||
this.setState({
|
||||
enableStatic: checked,
|
||||
|
@ -203,7 +203,7 @@ class WordsetGraph extends React.Component {
|
|||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{/*<Row>*/}
|
||||
{/* <Row>*/}
|
||||
{/* <Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>*/}
|
||||
{/* 显示曲线:*/}
|
||||
{/* </Col>*/}
|
||||
|
@ -214,14 +214,14 @@ class WordsetGraph extends React.Component {
|
|||
{/* });*/}
|
||||
{/* }} />*/}
|
||||
{/* </Col>*/}
|
||||
{/*</Row>*/}
|
||||
{/* </Row>*/}
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
启用粗线:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<Switch checked={this.state.enableBolderLink} onChange={(checked, e) => {
|
||||
this.setState({
|
||||
enableBolderLink: checked,
|
||||
|
@ -230,12 +230,12 @@ class WordsetGraph extends React.Component {
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
启用3D:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<Switch checked={this.state.enable3D} onChange={(checked, e) => {
|
||||
this.setState({
|
||||
enable3D: checked,
|
||||
|
@ -243,7 +243,7 @@ class WordsetGraph extends React.Component {
|
|||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{/*<Row>*/}
|
||||
{/* <Row>*/}
|
||||
{/* <Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>*/}
|
||||
{/* 粒子数量:*/}
|
||||
{/* </Col>*/}
|
||||
|
@ -254,38 +254,38 @@ class WordsetGraph extends React.Component {
|
|||
{/* });*/}
|
||||
{/* })} />*/}
|
||||
{/* </Col>*/}
|
||||
{/*</Row>*/}
|
||||
{/* </Row>*/}
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
扩散度:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<Slider value={this.state.strength} dots={true} min={0} max={1000} onChange={(value => {
|
||||
this.setState({
|
||||
strength: value,
|
||||
});
|
||||
|
||||
// https://github.com/vasturiano/react-force-graph/issues/25
|
||||
fg.current.d3Force('charge').strength(-value);
|
||||
fg.current.d3Force("charge").strength(-value);
|
||||
})} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
最大距离:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<Slider value={this.state.distanceMax} dots={true} min={0} max={1000} onChange={(value => {
|
||||
this.setState({
|
||||
distanceMax: value,
|
||||
});
|
||||
|
||||
// https://github.com/vasturiano/react-force-graph/issues/25
|
||||
fg.current.d3Force('charge').distanceMax(value);
|
||||
fg.current.d3Force("charge").distanceMax(value);
|
||||
})} />
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -296,32 +296,32 @@ class WordsetGraph extends React.Component {
|
|||
</div>
|
||||
} type="inner">
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={8}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={8}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
算法:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={16}>
|
||||
<Select virtual={false} style={{width: '100%'}} value={"K-Means"} onChange={(value => {
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={16}>
|
||||
<Select virtual={false} style={{width: "100%"}} value={"K-Means"} onChange={(value => {
|
||||
this.setState({
|
||||
algorithm: value,
|
||||
});
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: 'K-Means', name: 'K-Means'},
|
||||
{id: "K-Means", name: "K-Means"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
聚类个数:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<InputNumber style={{width: "100%"}} min={2} max={this.state.graph?.nodes.length} step={1} value={this.state.clusterNumber} onChange={value => {
|
||||
this.setState({
|
||||
clusterNumber: value,
|
||||
|
@ -330,12 +330,12 @@ class WordsetGraph extends React.Component {
|
|||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<span style={{verticalAlign: "middle"}}>
|
||||
距离上限:
|
||||
</span>
|
||||
</Col>
|
||||
<Col style={{marginTop: '5px', textAlign: 'center'}} span={12}>
|
||||
<Col style={{marginTop: "5px", textAlign: "center"}} span={12}>
|
||||
<InputNumber style={{width: "100%"}} min={1} max={100} step={1} value={this.state.distanceLimit} onChange={value => {
|
||||
this.setState({
|
||||
distanceLimit: value,
|
||||
|
@ -343,8 +343,8 @@ class WordsetGraph extends React.Component {
|
|||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{textAlign: "center", paddingTop: '10px'}}>
|
||||
<Button style={{margin: 'auto'}} type="primary" onClick={() => {
|
||||
<Row style={{textAlign: "center", paddingTop: "10px"}}>
|
||||
<Button style={{margin: "auto"}} type="primary" onClick={() => {
|
||||
this.setState({
|
||||
graph: null,
|
||||
});
|
||||
|
@ -352,7 +352,7 @@ class WordsetGraph extends React.Component {
|
|||
}}>
|
||||
重新聚类
|
||||
</Button>
|
||||
<Button style={{margin: 'auto', marginTop: '10px'}} onClick={() => {
|
||||
<Button style={{margin: "auto", marginTop: "10px"}} onClick={() => {
|
||||
this.downloadClusters();
|
||||
}}>
|
||||
下载结果
|
||||
|
@ -361,7 +361,7 @@ class WordsetGraph extends React.Component {
|
|||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getNodeId(node) {
|
||||
|
@ -379,7 +379,7 @@ class WordsetGraph extends React.Component {
|
|||
<div className="App">
|
||||
<Spin size="large" tip={i18next.t("general:Loading...")} style={{paddingTop: "10%"}} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.graph === null || this.state.graph.nodes === null || this.state.graph.links === null) {
|
||||
|
@ -387,7 +387,7 @@ class WordsetGraph extends React.Component {
|
|||
<div className="App">
|
||||
<Empty style={{paddingTop: "10%"}} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// highlight example
|
||||
|
@ -445,12 +445,12 @@ class WordsetGraph extends React.Component {
|
|||
if (this.state.selectedId === node.id) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, node.val * 1.7, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fill();
|
||||
} else if (this.state.selectedIds.filter(n => n.id === node.id).length > 0) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(node.x, node.y, node.val * 1.4, 0, 2 * Math.PI, false);
|
||||
ctx.fillStyle = 'red';
|
||||
ctx.fillStyle = "red";
|
||||
ctx.fill();
|
||||
} else if (this.state.selectedId !== null) {
|
||||
ctx.globalAlpha = 0.3;
|
||||
|
@ -460,20 +460,20 @@ class WordsetGraph extends React.Component {
|
|||
ctx.fillStyle = node.color;
|
||||
ctx.arc(node.x, node.y, node.val, 0, 2 * Math.PI, false);
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = 'rgb(255,255,255)';
|
||||
ctx.strokeStyle = "rgb(255,255,255)";
|
||||
ctx.stroke();
|
||||
|
||||
ctx.font = '5px Lucida Console';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillStyle = 'black'; //node.color;
|
||||
ctx.font = "5px Lucida Console";
|
||||
ctx.textAlign = "center";
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = "black"; // node.color;
|
||||
|
||||
ctx.fillText(node.name, node.x, node.y + 10);
|
||||
|
||||
ctx.restore();
|
||||
}}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -27,8 +27,8 @@ class WordsetGraphPage extends React.Component {
|
|||
|
||||
render() {
|
||||
return (this.state.wordset === undefined || this.state.wordset === null) ? null : (
|
||||
<WordsetGraph wordset={this.state.wordset} wordsetName={this.state.wordset.name}/>
|
||||
)
|
||||
<WordsetGraph wordset={this.state.wordset} wordsetName={this.state.wordset.name} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
||||
import {Button, Col, Popconfirm, Row, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as WordsetBackend from "./backend/WordsetBackend";
|
||||
|
@ -37,14 +37,14 @@ class WordsetListPage extends React.Component {
|
|||
distanceLimit: 14,
|
||||
vectorset: "wordVector_utf-8",
|
||||
vectors: [],
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
addWordset() {
|
||||
const newWordset = this.newWordset();
|
||||
WordsetBackend.addWordset(newWordset)
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Wordset added successfully`);
|
||||
Setting.showMessage("success", "Wordset added successfully");
|
||||
this.setState({
|
||||
wordsets: Setting.prependRow(this.state.wordsets, newWordset),
|
||||
});
|
||||
|
@ -57,7 +57,7 @@ class WordsetListPage extends React.Component {
|
|||
deleteWordset(i) {
|
||||
WordsetBackend.deleteWordset(this.state.wordsets[i])
|
||||
.then((res) => {
|
||||
Setting.showMessage("success", `Wordset deleted successfully`);
|
||||
Setting.showMessage("success", "Wordset deleted successfully");
|
||||
this.setState({
|
||||
wordsets: Setting.deleteRow(this.state.wordsets, i),
|
||||
});
|
||||
|
@ -71,34 +71,34 @@ class WordsetListPage extends React.Component {
|
|||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
width: '120px',
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "120px",
|
||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/wordsets/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: 'displayName',
|
||||
key: 'displayName',
|
||||
width: '200px',
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
width: "200px",
|
||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
||||
},
|
||||
{
|
||||
title: i18next.t("wordset:Words"),
|
||||
dataIndex: 'vectors',
|
||||
key: 'vectors',
|
||||
dataIndex: "vectors",
|
||||
key: "vectors",
|
||||
// width: '120px',
|
||||
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text);
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: i18next.t("wordset:All words"),
|
||||
|
@ -122,58 +122,58 @@ class WordsetListPage extends React.Component {
|
|||
// },
|
||||
{
|
||||
title: i18next.t("wordset:Vectorset"),
|
||||
dataIndex: 'vectorset',
|
||||
key: 'vectorset',
|
||||
width: '140px',
|
||||
dataIndex: "vectorset",
|
||||
key: "vectorset",
|
||||
width: "140px",
|
||||
sorter: (a, b) => a.vectorset.localeCompare(b.vectorset),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/vectorsets/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("wordset:Matched"),
|
||||
dataIndex: 'matched',
|
||||
key: 'matched',
|
||||
width: '140px',
|
||||
dataIndex: "matched",
|
||||
key: "matched",
|
||||
width: "140px",
|
||||
render: (text, record, index) => {
|
||||
const allWords = record.vectors.length;
|
||||
const validWords = record.vectors.filter(vector => vector.data.length !== 0).length;
|
||||
return `${Setting.getPercentage(allWords === 0 ? 0 : validWords / allWords)}% (${validWords} / ${allWords})`;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("wordset:Distance limit"),
|
||||
dataIndex: 'distanceLimit',
|
||||
key: 'distanceLimit',
|
||||
width: '120px',
|
||||
dataIndex: "distanceLimit",
|
||||
key: "distanceLimit",
|
||||
width: "120px",
|
||||
sorter: (a, b) => a.distanceLimit - b.distanceLimit,
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: 'action',
|
||||
key: 'action',
|
||||
width: '80px',
|
||||
dataIndex: "action",
|
||||
key: "action",
|
||||
width: "80px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} onClick={() => Setting.openLink(`/wordsets/${record.name}/graph`)}>{i18next.t("general:Result")}</Button>
|
||||
<Button style={{marginBottom: '10px', marginRight: '10px'}} onClick={() => Setting.downloadXlsx(record)}>{i18next.t("general:Download")}</Button>
|
||||
<Button style={{marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/wordsets/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => Setting.openLink(`/wordsets/${record.name}/graph`)}>{i18next.t("general:Result")}</Button>
|
||||
<Button style={{marginBottom: "10px", marginRight: "10px"}} onClick={() => Setting.downloadXlsx(record)}>{i18next.t("general:Download")}</Button>
|
||||
<Button style={{marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/wordsets/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
title={`Sure to delete wordset: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteWordset(index)}
|
||||
okText="OK"
|
||||
cancelText="Cancel"
|
||||
>
|
||||
<Button style={{marginBottom: '10px'}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
<Button style={{marginBottom: "10px"}} type="danger">{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -2,21 +2,21 @@ import * as Setting from "../Setting";
|
|||
|
||||
export function getAccount() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-account`, {
|
||||
method: 'GET',
|
||||
credentials: 'include'
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function signin(code, state) {
|
||||
return fetch(`${Setting.ServerUrl}/api/signin?code=${code}&state=${state}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function signout() {
|
||||
return fetch(`${Setting.ServerUrl}/api/signout`, {
|
||||
method: 'POST',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
import * as Setting from "../Setting";
|
||||
|
||||
export function updateFile(storeId, name, file) {
|
||||
let newFile = Setting.deepCopy(file);
|
||||
const newFile = Setting.deepCopy(file);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-file?store=${storeId}&name=${name}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newFile),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addFile(storeId, key, isLeaf, filename, file) {
|
||||
let formData = new FormData();
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-file?store=${storeId}&key=${key}&isLeaf=${isLeaf ? 1 : 0}&filename=${filename}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: formData,
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteFile(storeId, key, isLeaf) {
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-file?store=${storeId}&key=${key}&isLeaf=${isLeaf ? 1 : 0}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function activateFile(key, filename) {
|
||||
return fetch(`${Setting.ServerUrl}/api/activate-file?key=${key}&filename=${filename}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -3,47 +3,47 @@ import * as Setting from "../Setting";
|
|||
export function getGlobalPermissions() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-permissions`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getPermissions(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-permissions?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getPermission(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-permission?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updatePermission(owner, name, permission) {
|
||||
let newPermission = Setting.deepCopy(permission);
|
||||
const newPermission = Setting.deepCopy(permission);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-permission?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newPermission),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addPermission(permission) {
|
||||
let newPermission = Setting.deepCopy(permission);
|
||||
const newPermission = Setting.deepCopy(permission);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-permission`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newPermission),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deletePermission(permission) {
|
||||
let newPermission = Setting.deepCopy(permission);
|
||||
const newPermission = Setting.deepCopy(permission);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-permission`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newPermission),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -3,47 +3,47 @@ import * as Setting from "../Setting";
|
|||
export function getGlobalStores() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-stores`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getStores(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-stores?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getStore(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-store?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateStore(owner, name, store) {
|
||||
let newStore = Setting.deepCopy(store);
|
||||
const newStore = Setting.deepCopy(store);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-store?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newStore),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addStore(store) {
|
||||
let newStore = Setting.deepCopy(store);
|
||||
const newStore = Setting.deepCopy(store);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-store`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newStore),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteStore(store) {
|
||||
let newStore = Setting.deepCopy(store);
|
||||
const newStore = Setting.deepCopy(store);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-store`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newStore),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -3,54 +3,54 @@ import * as Setting from "../Setting";
|
|||
export function getGlobalVectorsets() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-vectorsets`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVectorsets(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-vectorsets?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVectorset(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-vectorset?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVectorsetGraph(owner, name, clusterNumber, distanceLimit) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-vectorset-graph?id=${owner}/${encodeURIComponent(name)}&clusterNumber=${clusterNumber}&distanceLimit=${distanceLimit}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateVectorset(owner, name, vectorset) {
|
||||
let newVectorset = Setting.deepCopy(vectorset);
|
||||
const newVectorset = Setting.deepCopy(vectorset);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-vectorset?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newVectorset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addVectorset(vectorset) {
|
||||
let newVectorset = Setting.deepCopy(vectorset);
|
||||
const newVectorset = Setting.deepCopy(vectorset);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-vectorset`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newVectorset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteVectorset(vectorset) {
|
||||
let newVectorset = Setting.deepCopy(vectorset);
|
||||
const newVectorset = Setting.deepCopy(vectorset);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-vectorset`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newVectorset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -3,54 +3,54 @@ import * as Setting from "../Setting";
|
|||
export function getGlobalVideos() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-videos`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVideos(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-videos?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVideo(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-video?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getVideoGraph(owner, name, clusterNumber, distanceLimit) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-video-graph?id=${owner}/${encodeURIComponent(name)}&clusterNumber=${clusterNumber}&distanceLimit=${distanceLimit}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateVideo(owner, name, video) {
|
||||
let newVideo = Setting.deepCopy(video);
|
||||
const newVideo = Setting.deepCopy(video);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-video?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newVideo),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addVideo(video) {
|
||||
let newVideo = Setting.deepCopy(video);
|
||||
const newVideo = Setting.deepCopy(video);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-video`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newVideo),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteVideo(video) {
|
||||
let newVideo = Setting.deepCopy(video);
|
||||
const newVideo = Setting.deepCopy(video);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-video`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newVideo),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -3,61 +3,61 @@ import * as Setting from "../Setting";
|
|||
export function getGlobalWordsets() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-wordsets`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getWordsets(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-wordsets?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getWordset(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-wordset?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getWordsetGraph(owner, name, clusterNumber, distanceLimit) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-wordset-graph?id=${owner}/${encodeURIComponent(name)}&clusterNumber=${clusterNumber}&distanceLimit=${distanceLimit}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getWordsetMatch(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-wordset-match?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include"
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateWordset(owner, name, wordset) {
|
||||
let newWordset = Setting.deepCopy(wordset);
|
||||
const newWordset = Setting.deepCopy(wordset);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-wordset?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newWordset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addWordset(wordset) {
|
||||
let newWordset = Setting.deepCopy(wordset);
|
||||
const newWordset = Setting.deepCopy(wordset);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-wordset`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newWordset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteWordset(wordset) {
|
||||
let newWordset = Setting.deepCopy(wordset);
|
||||
const newWordset = Setting.deepCopy(wordset);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-wordset`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newWordset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ i18n.init({
|
|||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
//debug: true,
|
||||
// debug: true,
|
||||
saveMissing: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -2,25 +2,25 @@
|
|||
// https://www.cnblogs.com/xuexia/p/12092768.html
|
||||
// react-app-polyfill
|
||||
// https://www.npmjs.com/package/react-app-polyfill
|
||||
import 'react-app-polyfill/ie9';
|
||||
import 'react-app-polyfill/stable';
|
||||
import "react-app-polyfill/ie9";
|
||||
import "react-app-polyfill/stable";
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import './font.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import "./font.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
// import 'antd/dist/antd.min.css';
|
||||
import {BrowserRouter} from 'react-router-dom';
|
||||
import './i18n';
|
||||
import {BrowserRouter} from "react-router-dom";
|
||||
import "./i18n";
|
||||
|
||||
ReactDOM.render((
|
||||
<BrowserRouter>
|
||||
<App/>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
),
|
||||
document.getElementById('root'));
|
||||
),
|
||||
document.getElementById("root"));
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
window.location.hostname === "localhost" ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
window.location.hostname === "[::1]" ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
|
@ -21,7 +21,7 @@ const isLocalhost = Boolean(
|
|||
);
|
||||
|
||||
export function register(config) {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
|
@ -31,7 +31,7 @@ export function register(config) {
|
|||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
window.addEventListener("load", () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
|
@ -42,8 +42,8 @@ export function register(config) {
|
|||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
||||
"This web app is being served cache-first by a service " +
|
||||
"worker. To learn more, visit https://bit.ly/CRA-PWA"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
|
@ -64,14 +64,14 @@ function registerValidSW(swUrl, config) {
|
|||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (installingWorker.state === "installed") {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the updated precached content has been fetched,
|
||||
// but the previous service worker will still serve the older
|
||||
// content until all client tabs are closed.
|
||||
console.log(
|
||||
'New content is available and will be used when all ' +
|
||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
||||
"New content is available and will be used when all " +
|
||||
"tabs for this page are closed. See https://bit.ly/CRA-PWA."
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
|
@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) {
|
|||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
console.log("Content is cached for offline use.");
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
|
@ -94,7 +94,7 @@ function registerValidSW(swUrl, config) {
|
|||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
console.error("Error during service worker registration:", error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -103,10 +103,10 @@ function checkValidServiceWorker(swUrl, config) {
|
|||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get('content-type');
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
||||
(contentType != null && contentType.indexOf("javascript") === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
|
@ -121,13 +121,13 @@ function checkValidServiceWorker(swUrl, config) {
|
|||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
"No internet connection found. App is running in offline mode."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue