feat: add some base file
This commit is contained in:
parent
40156944c4
commit
6cd8352971
|
@ -0,0 +1,372 @@
|
|||
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";
|
||||
import * as Conf from "./Conf";
|
||||
import HomePage from "./HomePage";
|
||||
import DatasetListPage from "./DatasetListPage";
|
||||
import DatasetEditPage from "./DatasetEditPage";
|
||||
import SigninPage from "./SigninPage";
|
||||
import i18next from "i18next";
|
||||
import PendingTaskPage from "./PendingTaskPage";
|
||||
import ProjectManagementPage from "./ProjectManagementPage";
|
||||
import PropositionPaperPage from "./PropositionPaperPage";
|
||||
|
||||
const {Header, Footer} = Layout;
|
||||
|
||||
const IconFont = createFromIconfontCN({
|
||||
scriptUrl: "//at.alicdn.com/t/font_2680620_ffij16fkwdg.js",
|
||||
});
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
selectedMenuKey: 0,
|
||||
account: undefined,
|
||||
uri: null,
|
||||
};
|
||||
|
||||
Setting.initServerUrl();
|
||||
Setting.initCasdoorSdk(Conf.AuthConfig);
|
||||
}
|
||||
|
||||
getRemSize = () => {
|
||||
let whdef = 100 / 1920;
|
||||
let wW = window.innerWidth;
|
||||
let rem = wW * whdef;
|
||||
document.documentElement.style.fontSize = rem + "px";
|
||||
}
|
||||
|
||||
componentDidMount = () => {
|
||||
window.resize = () => {
|
||||
this.getRemSize();
|
||||
};
|
||||
this.getRemSize();
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.updateMenuKey();
|
||||
this.getAccount();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const uri = location.pathname;
|
||||
if (this.state.uri !== uri) {
|
||||
this.updateMenuKey();
|
||||
}
|
||||
}
|
||||
|
||||
updateMenuKey() {
|
||||
const uri = location.pathname;
|
||||
this.setState({
|
||||
uri: uri,
|
||||
});
|
||||
if (uri === "/") {
|
||||
this.setState({selectedMenuKey: "/"});
|
||||
} else if (uri.includes("/datasets")) {
|
||||
this.setState({selectedMenuKey: "/datasets"});
|
||||
} else if (uri.includes("/pending-tasks")) {
|
||||
this.setState({selectedMenuKey: "/pending-tasks"});
|
||||
} else if (uri.includes("/project-management")) {
|
||||
this.setState({selectedMenuKey: "/project-management"});
|
||||
} else if (uri.includes("/proposition-paper")) {
|
||||
this.setState({selectedMenuKey: "/proposition-paper"});
|
||||
} else {
|
||||
this.setState({selectedMenuKey: "null"});
|
||||
}
|
||||
}
|
||||
|
||||
onUpdateAccount(account) {
|
||||
this.setState({
|
||||
account: account,
|
||||
});
|
||||
}
|
||||
|
||||
setLanguage(account) {
|
||||
// let language = account?.language;
|
||||
let language = localStorage.getItem("language");
|
||||
if (language !== "" && language !== i18next.language) {
|
||||
Setting.setLanguage(language);
|
||||
}
|
||||
}
|
||||
|
||||
getAccount() {
|
||||
AccountBackend.getAccount()
|
||||
.then((res) => {
|
||||
let account = res.data;
|
||||
if (account !== null) {
|
||||
this.setLanguage(account);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
account: account,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
signout() {
|
||||
AccountBackend.signout()
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
account: null,
|
||||
});
|
||||
|
||||
Setting.showMessage("success", "Successfully signed out, redirected to homepage");
|
||||
Setting.goToLink("/");
|
||||
// this.props.history.push("/");
|
||||
} else {
|
||||
Setting.showMessage("error", `Signout failed: ${res.msg}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleRightDropdownClick(e) {
|
||||
if (e.key === "/account") {
|
||||
Setting.openLink(Setting.getMyProfileUrl(this.state.account));
|
||||
} else if (e.key === "/logout") {
|
||||
this.signout();
|
||||
}
|
||||
}
|
||||
|
||||
renderAvatar() {
|
||||
if (this.state.account.avatar === "") {
|
||||
return (
|
||||
<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">
|
||||
{Setting.getShortName(this.state.account.name)}
|
||||
</Avatar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderRightDropdown() {
|
||||
const menu = (
|
||||
<Menu onClick={this.handleRightDropdownClick.bind(this)}>
|
||||
<Menu.Item key="/account">
|
||||
<SettingOutlined />
|
||||
{i18next.t("account:My Account")}
|
||||
</Menu.Item>
|
||||
<Menu.Item key="/logout">
|
||||
<LogoutOutlined />
|
||||
{i18next.t("account:Sign Out")}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown key="/rightDropDown" overlay={menu} className="rightDropDown">
|
||||
<div className="ant-dropdown-link" style={{float: "right", cursor: "pointer"}}>
|
||||
|
||||
|
||||
{
|
||||
this.renderAvatar()
|
||||
}
|
||||
|
||||
|
||||
{Setting.isMobile() ? null : Setting.getShortName(this.state.account.displayName)} <DownOutlined />
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
renderAccount() {
|
||||
let 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"}}>
|
||||
<a href={Setting.getSignupUrl()}>
|
||||
{i18next.t("account:Sign Up")}
|
||||
</a>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="/signin" style={{float: "right"}}>
|
||||
<a href={Setting.getSigninUrl()}>
|
||||
{i18next.t("account:Sign In")}
|
||||
</a>
|
||||
</Menu.Item>
|
||||
);
|
||||
res.push(
|
||||
<Menu.Item key="/" style={{float: "right"}}>
|
||||
<a href="/">
|
||||
{i18next.t("general:Home")}
|
||||
</a>
|
||||
</Menu.Item>
|
||||
);
|
||||
} else {
|
||||
res.push(this.renderRightDropdown());
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
let res = [];
|
||||
|
||||
if (this.state.account === null || this.state.account === undefined) {
|
||||
return [];
|
||||
}
|
||||
|
||||
res.push(
|
||||
<Menu.Item key="/">
|
||||
<a href="/">
|
||||
{i18next.t("general:Home")}
|
||||
</a>
|
||||
{/* <Link to="/">*/}
|
||||
{/* Home*/}
|
||||
{/* </Link>*/}
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
res.push(
|
||||
<Menu.Item key="/datasets">
|
||||
<Link to="/datasets">
|
||||
{i18next.t("general:Datasets")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
res.push(
|
||||
<Menu.Item key="/pending-tasks">
|
||||
<Link to="/pending-tasks">
|
||||
{i18next.t("general:Pending Tasks")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
res.push(
|
||||
<Menu.Item key="/proposition-paper">
|
||||
<Link to="/proposition-paper">
|
||||
{i18next.t("general:Proposition Paper")}
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
renderHomeIfSignedIn(component) {
|
||||
if (this.state.account !== null && this.state.account !== undefined) {
|
||||
return <Redirect to="/" />;
|
||||
} else {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
renderSigninIfNotSignedIn(component) {
|
||||
if (this.state.account === null) {
|
||||
sessionStorage.setItem("from", window.location.pathname);
|
||||
return <Redirect to="/signin" />;
|
||||
} else if (this.state.account === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
return (
|
||||
<div>
|
||||
<Header style={{padding: "0", marginBottom: "3px"}}>
|
||||
{
|
||||
Setting.isMobile() ? null : <a className="logo" href={"/"} />
|
||||
}
|
||||
<Menu
|
||||
// theme="dark"
|
||||
mode={"horizontal"}
|
||||
selectedKeys={[`${this.state.selectedMenuKey}`]}
|
||||
style={{lineHeight: "64px"}}
|
||||
>
|
||||
{
|
||||
this.renderMenu()
|
||||
}
|
||||
{
|
||||
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");}}>
|
||||
<IconFont type="icon-en" />
|
||||
|
||||
English
|
||||
|
||||
|
||||
</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");}}>
|
||||
<IconFont type="icon-zh" />
|
||||
|
||||
中文
|
||||
|
||||
|
||||
</div>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
</Header>
|
||||
<Switch>
|
||||
<Route path="/callback" component={AuthCallback} />
|
||||
<Route path="/signin" render={(props) => this.renderHomeIfSignedIn(<SigninPage {...props} />)} />
|
||||
<Route path="/pending-tasks" render={(props) => this.renderSigninIfNotSignedIn(<PendingTaskPage account={this.state.account} {...props} />)} />
|
||||
<Route path="/datasets" render={(props) => this.renderSigninIfNotSignedIn(<DatasetListPage account={this.state.account} {...props} />)} />
|
||||
<Route path="/datasets/:datasetName" render={(props) => this.renderSigninIfNotSignedIn(<DatasetEditPage account={this.state.account} {...props} />)} />
|
||||
<Route path="/project-management/:project_id/:role" render={(props) => this.renderSigninIfNotSignedIn(<ProjectManagementPage account={this.state.account} {...props} />)} />
|
||||
<Route path="/proposition-paper" render={(props) => this.renderSigninIfNotSignedIn(<PropositionPaperPage account={this.state.account} {...props} />)} />
|
||||
<Route path="/" render={(props) => <HomePage account={this.state.account} {...props} />} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
// How to keep your footer where it belongs ?
|
||||
// https://www.freecodecamp.org/neyarnws/how-to-keep-your-footer-where-it-belongs-59c6aa05c59c/
|
||||
|
||||
return (
|
||||
<Footer id="footer" style={
|
||||
{
|
||||
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://item.open-ct.com">OpenItem</a>, {Setting.isMobile() ? "Mobile" : "Desktop"} View
|
||||
</Footer>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div id="parent-area">
|
||||
<BackTop />
|
||||
<div id="content-wrap">
|
||||
{
|
||||
this.renderContent()
|
||||
}
|
||||
</div>
|
||||
{
|
||||
this.renderFooter()
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(App);
|
|
@ -0,0 +1,64 @@
|
|||
@import '~antd/dist/antd.less';
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #09d3ac;
|
||||
}
|
||||
|
||||
#parent-area {
|
||||
position: relative;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#content-wrap {
|
||||
padding-bottom: 70px; /* Footer height */
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 70px; /* Footer height */
|
||||
}
|
||||
|
||||
.ant-table-body {
|
||||
overflow-y: hidden !important
|
||||
}
|
||||
|
||||
.language_box {
|
||||
background: url("https://cdn.casbin.org/img/muti_language.svg");
|
||||
background-size: 25px, 25px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
width: 45px;
|
||||
height: 65px;
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.language_box:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
|
||||
.rightDropDown:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import React from "react";
|
||||
import {Button, Result, Spin} from "antd";
|
||||
import {withRouter} from "react-router-dom";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class AuthCallback extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
msg: null,
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.login();
|
||||
}
|
||||
|
||||
getFromLink() {
|
||||
const from = sessionStorage.getItem("from");
|
||||
if (from === null) {
|
||||
return "/";
|
||||
}
|
||||
return from;
|
||||
}
|
||||
|
||||
login() {
|
||||
Setting.signin().then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Logged in successfully");
|
||||
|
||||
const link = this.getFromLink();
|
||||
Setting.goToLink(link);
|
||||
} else {
|
||||
this.setState({
|
||||
msg: res.msg,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{textAlign: "center"}}>
|
||||
{this.state.msg === null ? (
|
||||
<Spin
|
||||
size="large"
|
||||
tip="Signing in..."
|
||||
style={{paddingTop: "10%"}}
|
||||
/>
|
||||
) : (
|
||||
<div style={{display: "inline"}}>
|
||||
<Result
|
||||
status="error"
|
||||
title="Login Error"
|
||||
subTitle={this.state.msg}
|
||||
extra={[
|
||||
<Button type="primary" key="details">
|
||||
Details
|
||||
</Button>,
|
||||
<Button key="help">Help</Button>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(AuthCallback);
|
|
@ -0,0 +1,11 @@
|
|||
export const AuthConfig = {
|
||||
// serverUrl: "https://door.casdoor.com",
|
||||
serverUrl: "http://localhost:7001",
|
||||
clientId: "eb78c88fc0697611f3e3",
|
||||
appName: "app-openitem",
|
||||
organizationName: "openct",
|
||||
redirectPath: "/callback",
|
||||
};
|
||||
|
||||
export const ForceLanguage = "";
|
||||
export const DefaultLanguage = "en";
|
|
@ -0,0 +1,206 @@
|
|||
import {message} from "antd";
|
||||
import {isMobile as isMobileDevice} from "react-device-detect";
|
||||
import i18next from "i18next";
|
||||
import Sdk from "casdoor-js-sdk";
|
||||
|
||||
export let ServerUrl = "";
|
||||
export let CasdoorSdk;
|
||||
|
||||
export function initServerUrl() {
|
||||
const hostname = window.location.hostname;
|
||||
if (hostname === "localhost") {
|
||||
ServerUrl = `http://${hostname}:8300`;
|
||||
}
|
||||
}
|
||||
|
||||
export function initCasdoorSdk(config) {
|
||||
CasdoorSdk = new Sdk(config);
|
||||
}
|
||||
|
||||
function getUrlWithLanguage(url) {
|
||||
if (url.includes("?")) {
|
||||
return `${url}&language=${getLanguage()}`;
|
||||
} else {
|
||||
return `${url}?language=${getLanguage()}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function getSignupUrl() {
|
||||
return getUrlWithLanguage(CasdoorSdk.getSignupUrl());
|
||||
}
|
||||
|
||||
export function getSigninUrl() {
|
||||
return getUrlWithLanguage(CasdoorSdk.getSigninUrl());
|
||||
}
|
||||
|
||||
export function getUserProfileUrl(userName, account) {
|
||||
return getUrlWithLanguage(CasdoorSdk.getUserProfileUrl(userName, account));
|
||||
}
|
||||
|
||||
export function getMyProfileUrl(account) {
|
||||
return getUrlWithLanguage(CasdoorSdk.getMyProfileUrl(account));
|
||||
}
|
||||
|
||||
export function signin() {
|
||||
return CasdoorSdk.signin(ServerUrl);
|
||||
}
|
||||
|
||||
export function parseJson(s) {
|
||||
if (s === "") {
|
||||
return null;
|
||||
} else {
|
||||
return JSON.parse(s);
|
||||
}
|
||||
}
|
||||
|
||||
export function myParseInt(i) {
|
||||
const res = parseInt(i);
|
||||
return isNaN(res) ? 0 : res;
|
||||
}
|
||||
|
||||
export function openLink(link) {
|
||||
// this.props.history.push(link);
|
||||
const w = window.open("about:blank");
|
||||
w.location.href = link;
|
||||
}
|
||||
|
||||
export function goToLink(link) {
|
||||
window.location.href = link;
|
||||
}
|
||||
|
||||
export function goToLinkSoft(ths, link) {
|
||||
ths.props.history.push(link);
|
||||
}
|
||||
|
||||
export function showMessage(type, text) {
|
||||
if (type === "") {
|
||||
return;
|
||||
} else if (type === "success") {
|
||||
message.success(text);
|
||||
} else if (type === "error") {
|
||||
message.error(text);
|
||||
}
|
||||
}
|
||||
|
||||
export function isAdminUser(account) {
|
||||
return account?.isAdmin;
|
||||
}
|
||||
|
||||
export function deepCopy(obj) {
|
||||
return Object.assign({}, obj);
|
||||
}
|
||||
|
||||
export function insertRow(array, row, i) {
|
||||
return [...array.slice(0, i), row, ...array.slice(i)];
|
||||
}
|
||||
|
||||
export function addRow(array, row) {
|
||||
return [...array, row];
|
||||
}
|
||||
|
||||
export function prependRow(array, row) {
|
||||
return [row, ...array];
|
||||
}
|
||||
|
||||
export function deleteRow(array, i) {
|
||||
// return array = array.slice(0, i).concat(array.slice(i + 1));
|
||||
return [...array.slice(0, i), ...array.slice(i + 1)];
|
||||
}
|
||||
|
||||
export function swapRow(array, i, j) {
|
||||
return [...array.slice(0, i), array[j], ...array.slice(i + 1, j), array[i], ...array.slice(j + 1)];
|
||||
}
|
||||
|
||||
export function isMobile() {
|
||||
// return getIsMobileView();
|
||||
return isMobileDevice;
|
||||
}
|
||||
|
||||
export function getFormattedDate(date) {
|
||||
if (date === undefined || date === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
date = date.replace("T", " ");
|
||||
date = date.replace("+08:00", " ");
|
||||
return date;
|
||||
}
|
||||
|
||||
export function getFormattedDateShort(date) {
|
||||
return date.slice(0, 10);
|
||||
}
|
||||
|
||||
export function getShortName(s) {
|
||||
return s.split("/").slice(-1)[0];
|
||||
}
|
||||
|
||||
export function getShortText(s, maxLength = 35) {
|
||||
if (s.length > maxLength) {
|
||||
return `${s.slice(0, maxLength)}...`;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
function getRandomInt(s) {
|
||||
let hash = 0;
|
||||
if (s.length !== 0) {
|
||||
for (let i = 0; i < s.length; i++) {
|
||||
let char = s.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash;
|
||||
}
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
export function getAvatarColor(s) {
|
||||
const colorList = ["#f56a00", "#7265e6", "#ffbf00", "#00a2ae"];
|
||||
let random = getRandomInt(s);
|
||||
if (random < 0) {
|
||||
random = -random;
|
||||
}
|
||||
return colorList[random % 4];
|
||||
}
|
||||
|
||||
export function getLanguage() {
|
||||
return i18next.language;
|
||||
}
|
||||
|
||||
export function setLanguage(language) {
|
||||
localStorage.setItem("language", language);
|
||||
changeMomentLanguage(language);
|
||||
i18next.changeLanguage(language);
|
||||
}
|
||||
|
||||
export function changeLanguage(language) {
|
||||
localStorage.setItem("language", language);
|
||||
changeMomentLanguage(language);
|
||||
i18next.changeLanguage(language);
|
||||
window.location.reload(true);
|
||||
}
|
||||
|
||||
export function changeMomentLanguage(lng) {
|
||||
return;
|
||||
// if (lng === "zh") {
|
||||
// moment.locale("zh", {
|
||||
// relativeTime: {
|
||||
// future: "%s内",
|
||||
// past: "%s前",
|
||||
// s: "几秒",
|
||||
// ss: "%d秒",
|
||||
// m: "1分钟",
|
||||
// mm: "%d分钟",
|
||||
// h: "1小时",
|
||||
// hh: "%d小时",
|
||||
// d: "1天",
|
||||
// dd: "%d天",
|
||||
// M: "1个月",
|
||||
// MM: "%d个月",
|
||||
// y: "1年",
|
||||
// yy: "%d年",
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
class SigninPage extends React.Component {
|
||||
componentDidMount() {
|
||||
window.location.replace(Setting.getSigninUrl());
|
||||
}
|
||||
|
||||
render() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export default SigninPage;
|
|
@ -0,0 +1,22 @@
|
|||
import * as Setting from "../Setting";
|
||||
|
||||
export function getAccount() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-account`, {
|
||||
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",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function signout() {
|
||||
return fetch(`${Setting.ServerUrl}/api/signout`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
import * as Setting from "../Setting";
|
||||
|
||||
export function getGlobalDatasets() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-global-datasets`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getDatasets(owner) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-datasets?owner=${owner}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getDataset(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-dataset?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateDataset(owner, name, dataset) {
|
||||
let newDataset = Setting.deepCopy(dataset);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-dataset?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newDataset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addDataset(dataset) {
|
||||
let newDataset = Setting.deepCopy(dataset);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-dataset`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newDataset),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteDataset(dataset) {
|
||||
let newDataset = Setting.deepCopy(dataset);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-dataset`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newDataset),
|
||||
}).then(res => res.json());
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
import * as Setting from "../Setting";
|
||||
|
||||
export function GetDetailedInfo(pid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj/detailed?:pid=${pid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetBasicInfo(pid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj/basic?:pid=${pid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function UpdateProjectInfo() {
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj`, {
|
||||
method: "PUT",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function CreatTemplateProject(data) {
|
||||
let project = new Object();
|
||||
project.basic_info = data;
|
||||
project.name = data.name;
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj/template`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(project),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function CreateEmptyProject() {
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetUserAssignments(uid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj/user?:uid=${uid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetProjectList(id_list) {
|
||||
let data = new Object();
|
||||
data.id_list = id_list;
|
||||
return fetch(`${Setting.ServerUrl}/api/review/query/proj`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetProjectAssignments(pid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/review/proj/assign?:pid=${pid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetUserList(id_list) {
|
||||
let data = new Object();
|
||||
data.id_list = id_list;
|
||||
return fetch(`${Setting.ServerUrl}/api/review/query/user`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
}).then(res => res.json());
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import * as Setting from "../Setting";
|
||||
|
||||
export function TraceQuestionVersion(qid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/qbank/question/trace?:qid=${qid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetTempQuestionList(id_list) {
|
||||
let data = new Object();
|
||||
data.id_list = id_list;
|
||||
return fetch(`${Setting.ServerUrl}/api/qbank/query/t_question`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(data),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetUserTempQuestions(uid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/qbank/question/user_t?:uid=${uid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function GetUserTempTestpaper(uid) {
|
||||
return fetch(`${Setting.ServerUrl}/api/qbank/testpaper/user_t?:uid=${uid}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function CreateNewQuestion(data) {
|
||||
let newData = Setting.deepCopy(data);
|
||||
return fetch(`${Setting.ServerUrl}/api/qbank/question`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newData),
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function CreateNewTestpaper(data) {
|
||||
let newData = Setting.deepCopy(data);
|
||||
return fetch(`${Setting.ServerUrl}/api/qbank/testpaper`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newData),
|
||||
}).then(res => res.json());
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/* 默认字体 具体可能需要修改 */
|
||||
#editor {
|
||||
font-family: 'Microsoft-YaHei';
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimSun]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimSun]::before {
|
||||
content: "宋体";
|
||||
font-family: "SimSun";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=SimHei]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=SimHei]::before {
|
||||
content: "黑体";
|
||||
font-family: "SimHei";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Microsoft-YaHei]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Microsoft-YaHei]::before {
|
||||
content: "微软雅黑";
|
||||
font-family: "Microsoft YaHei";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=consolas]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=consolas]::before {
|
||||
content: "consolas";
|
||||
font-family: "consolas";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=KaiTi]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=KaiTi]::before {
|
||||
content: "楷体";
|
||||
font-family: "KaiTi";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=FangSong]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=FangSong]::before {
|
||||
content: "仿宋";
|
||||
font-family: "FangSong";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Arial]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Arial]::before {
|
||||
content: "Arial";
|
||||
font-family: "Arial";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=Times-New-Roman]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=Times-New-Roman]::before {
|
||||
content: "New Roman";
|
||||
font-family: "Times New Roman";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=sans-serif]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=sans-serif]::before {
|
||||
content: "sans-serif";
|
||||
font-family: "sans-serif";
|
||||
}
|
||||
|
||||
.ql-font-SimSun {
|
||||
font-family: "SimSun";
|
||||
}
|
||||
.ql-font-SimHei {
|
||||
font-family: "SimHei";
|
||||
}
|
||||
.ql-font-Microsoft-YaHei {
|
||||
font-family: "Microsoft YaHei";
|
||||
}
|
||||
.ql-font-KaiTi {
|
||||
font-family: "KaiTi";
|
||||
}
|
||||
.ql-font-FangSong {
|
||||
font-family: "FangSong";
|
||||
}
|
||||
.ql-font-Arial {
|
||||
font-family: "Arial";
|
||||
}
|
||||
.ql-font-Times-New-Roman {
|
||||
font-family: "Times New Roman";
|
||||
}
|
||||
.ql-font-sans-serif {
|
||||
font-family: "sans-serif";
|
||||
}
|
||||
.ql-font-consolas {
|
||||
font-family: "consolas";
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import i18n from "i18next";
|
||||
import zh from "./locales/zh/data.json";
|
||||
import en from "./locales/en/data.json";
|
||||
import * as Conf from "./Conf";
|
||||
import * as Setting from "./Setting";
|
||||
|
||||
const resources = {
|
||||
en: en,
|
||||
zh: zh,
|
||||
};
|
||||
|
||||
function initLanguage() {
|
||||
let language = localStorage.getItem("language");
|
||||
if (language === undefined || language == null) {
|
||||
if (Conf.ForceLanguage !== "") {
|
||||
language = Conf.ForceLanguage;
|
||||
} else {
|
||||
let userLanguage;
|
||||
userLanguage = navigator.language;
|
||||
switch (userLanguage) {
|
||||
case "zh-CN":
|
||||
language = "zh";
|
||||
break;
|
||||
case "zh":
|
||||
language = "zh";
|
||||
break;
|
||||
case "en":
|
||||
language = "en";
|
||||
break;
|
||||
case "en-US":
|
||||
language = "en";
|
||||
break;
|
||||
default:
|
||||
language = Conf.DefaultLanguage;
|
||||
}
|
||||
}
|
||||
}
|
||||
Setting.changeMomentLanguage(language);
|
||||
|
||||
return language;
|
||||
}
|
||||
|
||||
i18n.init({
|
||||
lng: initLanguage(),
|
||||
|
||||
resources: resources,
|
||||
|
||||
keySeparator: false,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
// debug: true,
|
||||
saveMissing: true,
|
||||
});
|
||||
|
||||
export default i18n;
|
|
@ -0,0 +1,80 @@
|
|||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif !important;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
}
|
||||
|
||||
.logo {
|
||||
background: url("https://cdn.open-ct.com/logo/openct_logo_1082x328.png");
|
||||
background-size: 108px, 33px;
|
||||
width: 108px;
|
||||
height: 33px;
|
||||
/*background: rgba(0, 0, 0, 0.2);*/
|
||||
margin: 17px 10px 16px 20px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.ant-table.ant-table-middle .ant-table-title, .ant-table.ant-table-middle .ant-table-footer, .ant-table.ant-table-middle thead > tr > th, .ant-table.ant-table-middle tbody > tr > td {
|
||||
padding: 1px 8px !important;
|
||||
}
|
||||
|
||||
.ant-list-sm .ant-list-item {
|
||||
padding: 2px !important;
|
||||
}
|
||||
|
||||
.highlight-row {
|
||||
background: rgb(249,198,205);
|
||||
}
|
||||
|
||||
.alert-row {
|
||||
background: #ffccc7;
|
||||
}
|
||||
|
||||
/*http://react-china.org/t/topic/33846/3*/
|
||||
.ant-table-header {
|
||||
scrollbar-color:transparent transparent
|
||||
}
|
||||
.ant-table-header::-webkit-scrollbar {
|
||||
background-color:transparent
|
||||
}
|
||||
|
||||
.import-output {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
.App {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.red-row {
|
||||
background: #ffccc7;
|
||||
}
|
||||
|
||||
.yellow-row {
|
||||
background: #ffffb8;
|
||||
}
|
||||
|
||||
.green-row {
|
||||
background: #d9f7be;
|
||||
}
|
||||
|
||||
/*https://stackoverflow.com/questions/64961752/how-do-i-change-the-color-of-selected-menu-item-in-ant-design*/
|
||||
/*.conferenceMenu > .ant-menu-item-selected {*/
|
||||
/* background-color: rgb(230,247,255) !important;*/
|
||||
/*}*/
|
||||
|
||||
.conferenceMenu {
|
||||
background-color: rgb(242,242,242) !important;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// create-react-app + IE9
|
||||
// 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 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";
|
||||
|
||||
ReactDOM.render((
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
),
|
||||
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.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"account": {
|
||||
"My Account": "My Account",
|
||||
"Sign In": "Sign In",
|
||||
"Sign Out": "Sign Out",
|
||||
"Sign Up": "Sign Up"
|
||||
},
|
||||
"dataset": {
|
||||
"Address": "Address",
|
||||
"Carousels": "Carousels",
|
||||
"Default item": "Default item",
|
||||
"Edit Dataset": "Edit Dataset",
|
||||
"End date": "End date",
|
||||
"Full name": "Full name",
|
||||
"Introduction text": "Introduction text",
|
||||
"Language": "Language",
|
||||
"Location": "Location",
|
||||
"Organizer": "Organizer",
|
||||
"Start date": "Start date"
|
||||
},
|
||||
"general": {
|
||||
"Action": "Action",
|
||||
"Add": "Add",
|
||||
"Datasets": "Datasets",
|
||||
"Delete": "Delete",
|
||||
"Edit": "Edit",
|
||||
"Home": "Home",
|
||||
"Name": "Name",
|
||||
"Save": "Save",
|
||||
"Status": "Status",
|
||||
"Pendingtasks":"Pending Tasks",
|
||||
"Proposition Paper":"Proposition Paper"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"account": {
|
||||
"My Account": "我的账户",
|
||||
"Sign In": "登录",
|
||||
"Sign Out": "登出",
|
||||
"Sign Up": "注册"
|
||||
},
|
||||
"dataset": {
|
||||
"Address": "Address",
|
||||
"Carousels": "Carousels",
|
||||
"Default item": "Default item",
|
||||
"Edit Dataset": "Edit Dataset",
|
||||
"End date": "End date",
|
||||
"Full name": "Full name",
|
||||
"Introduction text": "Introduction text",
|
||||
"Language": "Language",
|
||||
"Location": "Location",
|
||||
"Organizer": "Organizer",
|
||||
"Start date": "Start date"
|
||||
},
|
||||
"general": {
|
||||
"Action": "操作",
|
||||
"Add": "添加",
|
||||
"Datasets": "数据集",
|
||||
"Delete": "删除",
|
||||
"Edit": "编辑",
|
||||
"Home": "首页",
|
||||
"Name": "名称",
|
||||
"Save": "保存",
|
||||
"Status": "状态",
|
||||
"Pending Tasks":"项目管理",
|
||||
"Proposition Paper":"命题组卷"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
// This optional code is used to register a service worker.
|
||||
// register() is not called by default.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on subsequent visits to a page, after all the
|
||||
// existing tabs open on the page have been closed, since previously cached
|
||||
// resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model and instructions on how to
|
||||
// opt-in, read https://bit.ly/CRA-PWA
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === "localhost" ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
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}$/
|
||||
)
|
||||
);
|
||||
|
||||
export function register(config) {
|
||||
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) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl, config);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// 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"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not localhost. Just register service worker
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl, config) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
if (installingWorker == null) {
|
||||
return;
|
||||
}
|
||||
installingWorker.onstatechange = () => {
|
||||
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."
|
||||
);
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onUpdate) {
|
||||
config.onUpdate(registration);
|
||||
}
|
||||
} else {
|
||||
// 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.");
|
||||
|
||||
// Execute callback
|
||||
if (config && config.onSuccess) {
|
||||
config.onSuccess(registration);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error during service worker registration:", error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl, config) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
const contentType = response.headers.get("content-type");
|
||||
if (
|
||||
response.status === 404 ||
|
||||
(contentType != null && contentType.indexOf("javascript") === -1)
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl, config);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
"No internet connection found. App is running in offline mode."
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue