feat: add .eslintrc and format all code (#11)

This commit is contained in:
Xinhao Yuan 2023-06-26 10:17:56 +08:00 committed by GitHub
parent 7c56c63893
commit 3aa25da75b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 976 additions and 867 deletions

112
web/.eslintrc Normal file
View File

@ -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"
}
}

View File

@ -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"}}>
&nbsp;
&nbsp;
{
@ -177,24 +177,24 @@ class App extends Component {
&nbsp;
</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");}}>
&nbsp;&nbsp;&nbsp;&nbsp;<IconFont type="icon-en" />
&nbsp;
English
@ -348,8 +347,8 @@ class App extends Component {
&nbsp;
</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");}}>
&nbsp;&nbsp;&nbsp;&nbsp;<IconFont type="icon-zh" />
&nbsp;
中文
@ -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()

View File

@ -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);
});

View File

@ -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"

View File

@ -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} />
);
}
}

View File

@ -13,4 +13,4 @@ export const DefaultWordsetName = "word";
export const ForceLanguage = "";
export const DefaultLanguage = "en";
export const AppUrl = ""
export const AppUrl = "";

View File

@ -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} />
);
}
}

View File

@ -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>
)
);
}
}

View File

@ -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>
)
);
}
}

View File

@ -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} />;
}
}
}

View File

@ -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>
)
);
}
}

View File

@ -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 => {

View File

@ -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());
}

View File

@ -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")}&nbsp;&nbsp;&nbsp;&nbsp;
<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 => {

View File

@ -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>
)
}
);
},
},
];

View File

@ -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>
)
);
}
}

View File

@ -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")}&nbsp;&nbsp;&nbsp;&nbsp;
<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 => {

View File

@ -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>
)
}
);
},
},
];

View File

@ -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>
)
);
}
}

View File

@ -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 (

View File

@ -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")}&nbsp;&nbsp;&nbsp;&nbsp;
<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 => {

View File

@ -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")}
{/*&nbsp;&nbsp;&nbsp;&nbsp;*/}
{/*<Button type="primary" size="small" onClick={this.addVideo.bind(this)}>{i18next.t("general:Add")}</Button>*/}
{/* &nbsp;&nbsp;&nbsp;&nbsp;*/}
{/* <Button type="primary" size="small" onClick={this.addVideo.bind(this)}>{i18next.t("general:Add")}</Button>*/}
&nbsp;&nbsp;&nbsp;&nbsp;
{
this.renderUpload()

View File

@ -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")}&nbsp;&nbsp;&nbsp;&nbsp;
<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 => {

View File

@ -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() {

View File

@ -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} />
);
}
}

View File

@ -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>
)
}
);
},
},
];

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -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());
}

View File

@ -50,7 +50,7 @@ i18n.init({
interpolation: {
escapeValue: false,
},
//debug: true,
// debug: true,
saveMissing: true,
});

View File

@ -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.

View File

@ -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();
});