Feat: support set custom timezone

This commit is contained in:
HFO4 2021-04-03 16:54:05 +08:00
parent 522a75c750
commit 08a301d53a
17 changed files with 210 additions and 67 deletions

View File

@ -27,6 +27,7 @@
"clsx": "latest",
"connected-react-router": "^6.9.1",
"css-loader": "2.1.1",
"dayjs": "^1.10.4",
"dotenv": "6.2.0",
"dotenv-expand": "5.1.0",
"eslint": "^6.8.0",
@ -52,7 +53,6 @@
"material-ui-toggle-icon": "^1.1.1",
"mdi-material-ui": "^6.9.0",
"mini-css-extract-plugin": "0.8.0",
"moment": "^2.29.1",
"monaco-editor-webpack-plugin": "^3.0.0",
"optimize-css-assets-webpack-plugin": "5.0.3",
"pnp-webpack-plugin": "1.5.0",

View File

@ -25,6 +25,7 @@ import { toggleSnackbar } from "../../../actions";
import API from "../../../middleware/Api";
import { sizeToString } from "../../../utils";
import FileFilter from "../Dialogs/FileFilter";
import { formatLocalTime } from "../../../utils/datetime";
const useStyles = makeStyles((theme) => ({
root: {
@ -424,13 +425,10 @@ export default function File() {
</Link>
</TableCell>
<TableCell>
{new Date(
row.CreatedAt
).toLocaleDateString() +
" " +
new Date(
row.CreatedAt
).toLocaleTimeString()}
{formatLocalTime(
row.CreatedAt,
"YYYY-MM-DD H:mm:ss"
)}
</TableCell>
<TableCell>
<Tooltip title={"删除"}>

View File

@ -23,6 +23,7 @@ import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../../actions";
import API from "../../../middleware/Api";
import ShareFilter from "../Dialogs/ShareFilter";
import { formatLocalTime } from "../../../utils/datetime";
const useStyles = makeStyles((theme) => ({
root: {
@ -445,13 +446,10 @@ export default function Share() {
</Link>
</TableCell>
<TableCell>
{new Date(
row.CreatedAt
).toLocaleDateString() +
" " +
new Date(
row.CreatedAt
).toLocaleTimeString()}
{formatLocalTime(
row.CreatedAt,
"YYYY-MM-DD H:mm:ss"
)}
</TableCell>
<TableCell>
<Tooltip title={"删除"}>

View File

@ -23,6 +23,7 @@ import { toggleSnackbar } from "../../../actions";
import API from "../../../middleware/Api";
import { sizeToString } from "../../../utils";
import ShareFilter from "../Dialogs/ShareFilter";
import { formatLocalTime } from "../../../utils/datetime";
const useStyles = makeStyles((theme) => ({
root: {
@ -355,13 +356,10 @@ export default function Download() {
</Link>
</TableCell>
<TableCell>
{new Date(
row.CreatedAt
).toLocaleDateString() +
" " +
new Date(
row.CreatedAt
).toLocaleTimeString()}
{formatLocalTime(
row.CreatedAt,
"YYYY-MM-DD H:mm:ss"
)}
</TableCell>
<TableCell>
<Tooltip title={"删除"}>

View File

@ -23,6 +23,7 @@ import { toggleSnackbar } from "../../../actions";
import { getTaskProgress, getTaskStatus, getTaskType } from "../../../config";
import API from "../../../middleware/Api";
import ShareFilter from "../Dialogs/ShareFilter";
import { formatLocalTime } from "../../../utils/datetime";
const useStyles = makeStyles((theme) => ({
root: {
@ -343,13 +344,10 @@ export default function Task() {
</Link>
</TableCell>
<TableCell>
{new Date(
row.CreatedAt
).toLocaleDateString() +
" " +
new Date(
row.CreatedAt
).toLocaleTimeString()}
{formatLocalTime(
row.CreatedAt,
"YYYY-MM-DD H:mm:ss"
)}
</TableCell>
<TableCell>
<Tooltip title={"删除"}>

View File

@ -585,7 +585,7 @@ export default function DownloadingCard(props) {
</Grid>
<Grid item xs={8} className={classes.infoValue}>
<TimeAgo
datetime={parseInt(task.update + "000")}
datetime={task.update}
locale="zh_CN"
/>
</Grid>

View File

@ -29,6 +29,7 @@ import API from "../../middleware/Api";
import { useDispatch } from "react-redux";
import { toggleSnackbar } from "../../actions";
import { useHistory } from "react-router";
import { formatLocalTime } from "../../utils/datetime";
const ExpansionPanel = withStyles({
root: {
@ -413,7 +414,10 @@ export default function FinishedCard(props) {
创建日期
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{props.task.create}
{formatLocalTime(
props.task.create,
"YYYY-MM-DD H:mm:ss"
)}
</Grid>
</Grid>
<Grid container xs={12} sm={6}>
@ -421,7 +425,10 @@ export default function FinishedCard(props) {
最后更新
</Grid>
<Grid item xs={8} className={classes.infoValue}>
{props.task.update}
{formatLocalTime(
props.task.update,
"YYYY-MM-DD H:mm:ss"
)}
</Grid>
</Grid>
</Grid>

View File

@ -16,12 +16,12 @@ import { filename, sizeToString } from "../../../utils";
import Link from "@material-ui/core/Link";
import Tooltip from "@material-ui/core/Tooltip";
import TimeAgo from "timeago-react";
import moment from "moment";
import ListLoading from "../../Placeholder/ListLoading";
import Hidden from "@material-ui/core/Hidden";
import Dialog from "@material-ui/core/Dialog";
import Slide from "@material-ui/core/Slide";
import AppBar from "@material-ui/core/AppBar";
import { formatLocalTime } from "../../../utils/datetime";
const drawerWidth = 350;
@ -181,13 +181,12 @@ export default function SideDrawer() {
{
label: "修改于",
value: (d, t) =>
moment.parseZone(d.updated_at).format("YYYY/MM/DD H:mm:ss"),
formatLocalTime(d.updated_at, "YYYY/MM/DD H:mm:ss"),
show: (d) => true,
},
{
label: "创建于",
value: (d) =>
moment.parseZone(d.created_at).format("YYYY/MM/DD H:mm:ss"),
value: (d) => formatLocalTime(d.created_at, "YYYY/MM/DD H:mm:ss"),
show: (d) => true,
},
];

View File

@ -20,6 +20,8 @@ import KeyboardReturnIcon from "@material-ui/icons/KeyboardReturn";
import CheckCircleRoundedIcon from "@material-ui/icons/CheckCircleRounded";
import statusHelper from "../../utils/page";
import Grow from "@material-ui/core/Grow";
import dayjs from "dayjs";
import { formatLocalTime } from "../../utils/datetime";
const styles = (theme) => ({
selected: {
@ -188,7 +190,10 @@ class TableRowCompoment extends Component {
})}
>
{" "}
{this.props.file.date}
{formatLocalTime(
this.props.file.date,
"YYYY-MM-DD H:mm:ss"
)}
</Typography>
</TableCell>
</TableRow>

View File

@ -0,0 +1,88 @@
import React, { useState, useCallback, useEffect } from "react";
import { FormLabel, makeStyles } from "@material-ui/core";
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
DialogContentText,
CircularProgress,
} from "@material-ui/core";
import { toggleSnackbar, setModalsLoading } from "../../actions/index";
import PathSelector from "../FileManager/PathSelector";
import { useDispatch } from "react-redux";
import API from "../../middleware/Api";
import TextField from "@material-ui/core/TextField";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
import {
refreshTimeZone,
timeZone,
validateTimeZone,
} from "../../utils/datetime";
import FormControl from "@material-ui/core/FormControl";
import Auth from "../../middleware/Auth";
const useStyles = makeStyles((theme) => ({}));
export default function TimeZoneDialog(props) {
const [timeZoneValue, setTimeZoneValue] = useState(timeZone);
const dispatch = useDispatch();
const ToggleSnackbar = useCallback(
(vertical, horizontal, msg, color) =>
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
[dispatch]
);
const saveZoneInfo = () => {
if (!validateTimeZone(timeZoneValue)) {
ToggleSnackbar("top", "right", "无效的时区名称", "warning");
return;
}
Auth.SetPreference("timeZone", timeZoneValue);
refreshTimeZone();
props.onClose();
};
const classes = useStyles();
return (
<Dialog
open={props.open}
onClose={props.onClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">更改时区</DialogTitle>
<DialogContent>
<FormControl>
<TextField
label={"IANA 时区名称标识"}
value={timeZoneValue}
onChange={(e) => setTimeZoneValue(e.target.value)}
/>
</FormControl>
</DialogContent>
<DialogActions>
<Button onClick={props.onClose}>取消</Button>
<div className={classes.wrapper}>
<Button
color="primary"
disabled={timeZoneValue === ""}
onClick={() => saveZoneInfo()}
>
确定
{props.modalsLoading && (
<CircularProgress
size={24}
className={classes.buttonProgress}
/>
)}
</Button>
</div>
</DialogActions>
</Dialog>
);
}

View File

@ -9,9 +9,9 @@ import TableRow from "@material-ui/core/TableRow";
import TableCell from "@material-ui/core/TableCell";
import TableBody from "@material-ui/core/TableBody";
import API from "../../middleware/Api";
import TimeAgo from "timeago-react";
import { getTaskProgress, getTaskStatus, getTaskType } from "../../config";
import Pagination from "@material-ui/lab/Pagination";
import { formatLocalTime } from "../../utils/datetime";
const useStyles = makeStyles((theme) => ({
layout: {
@ -119,10 +119,10 @@ export default function Tasks() {
component="th"
scope="row"
>
<TimeAgo
datetime={row.create_date}
locale="zh_CN"
/>
{formatLocalTime(
row.create_date,
"YYYY-MM-DD H:mm:ss"
)}
</TableCell>
<TableCell nowrap="nowrap" align="right">
{getTaskType(row.type)}

View File

@ -48,9 +48,11 @@ import API from "../../middleware/Api";
import Auth from "../../middleware/Auth";
import { withRouter } from "react-router";
import QRCode from "qrcode-react";
import { Brightness3, ListAlt, PermContactCalendar } from "@material-ui/icons";
import { Brightness3, ListAlt, PermContactCalendar,Schedule } from "@material-ui/icons";
import { transformTime } from "../../utils";
import Authn from "./Authn";
import { formatLocalTime, timeZone } from "../../utils/datetime";
import TimeZoneDialog from "../Modals/TimeZone";
const styles = (theme) => ({
layout: {
@ -197,6 +199,7 @@ class UserSettingCompoment extends Component {
changeWebDavPwd: false,
groupBackModal: false,
changePolicy: false,
changeTimeZone: false,
settings: {
uid: 0,
group_expires: 0,
@ -706,8 +709,9 @@ class UserSettingCompoment extends Component {
className={classes.infoText}
color="textSecondary"
>
{transformTime(
parseInt(user.created_at + "000")
{formatLocalTime(
user.created_at,
"YYYY-MM-DD H:mm:ss"
)}
</Typography>
</ListItemSecondaryAction>
@ -889,6 +893,32 @@ class UserSettingCompoment extends Component {
/>
</ListItemSecondaryAction>
</ListItem>
<Divider />
<ListItem
onClick={() =>
this.setState({ changeTimeZone: true })
}
button
>
<ListItemIcon className={classes.iconFix}>
<Schedule />
</ListItemIcon>
<ListItemText primary="时区" />
<ListItemSecondaryAction
className={classes.flexContainer}
>
<Typography
className={classes.infoTextWithIcon}
color="textSecondary"
>
{timeZone}
</Typography>
<RightIcon
className={classes.rightIconWithText}
/>
</ListItemSecondaryAction>
</ListItem>
</List>
</Paper>
{user.group.webdav && (
@ -976,6 +1006,10 @@ class UserSettingCompoment extends Component {
)}
<div className={classes.paddingBottom}></div>
</div>
<TimeZoneDialog
onClose={() => this.setState({ changeTimeZone: false })}
open={this.state.changeTimeZone}
/>
<Dialog
open={this.state.avatarModal}
onClose={this.handleClose}

View File

@ -3,6 +3,7 @@ import { makeStyles } from "@material-ui/core/styles";
import { Avatar, Typography } from "@material-ui/core";
import { useHistory } from "react-router";
import Link from "@material-ui/core/Link";
import { formatLocalTime } from "../../utils/datetime";
const useStyles = makeStyles((theme) => ({
boxHeader: {
@ -38,7 +39,7 @@ export default function Creator(props) {
}
return Math.round(props.share.expire / 3600) + " 小时后到期";
}
return props.share.create_date;
return formatLocalTime(props.share.create_date, "YYYY-MM-DD H:mm:ss");
};
const userProfile = () => {

View File

@ -36,6 +36,7 @@ import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import { withRouter } from "react-router-dom";
import ToggleIcon from "material-ui-toggle-icon";
import { formatLocalTime } from "../../utils/datetime";
const styles = (theme) => ({
cardContainer: {
@ -342,7 +343,10 @@ class MyShareCompoment extends Component {
}
subheader={
<span>
{value.create_date}
{formatLocalTime(
value.create_date,
"YYYY-MM-DD H:mm:ss"
)}
{this.isExpired(value) && (
<Chip
size="small"

28
src/utils/datetime.js Normal file
View File

@ -0,0 +1,28 @@
import dayjs from "dayjs";
import timezone from "dayjs/plugin/timezone";
import utc from "dayjs/plugin/utc";
import Auth from "../middleware/Auth";
dayjs.extend(utc);
dayjs.extend(timezone);
const defaultTimeZone = "Asia/Shanghai";
const preferTimeZone = Auth.GetPreference("timeZone");
export let timeZone = preferTimeZone ? preferTimeZone : defaultTimeZone;
export function refreshTimeZone() {
timeZone = Auth.GetPreference("timeZone");
timeZone = timeZone ? timeZone : defaultTimeZone;
}
export function formatLocalTime(time, format) {
return dayjs(time).tz(timeZone).format(format);
}
export function validateTimeZone(name) {
try {
dayjs().tz(name).format();
} catch (e) {
return false;
}
return true;
}

View File

@ -159,21 +159,6 @@ export function filename(path) {
return pathList.pop();
}
export function transformTime(timestamp = +new Date()) {
if (timestamp) {
const time = new Date(timestamp);
const y = time.getFullYear(); //getFullYear方法以四位数字返回年份
const M = time.getMonth() + 1; // getMonth方法从 Date 对象返回月份 (0 ~ 11),返回结果需要手动加一
const d = time.getDate(); // getDate方法从 Date 对象返回一个月中的某一天 (1 ~ 31)
const h = time.getHours(); // getHours方法返回 Date 对象的小时 (0 ~ 23)
const m = time.getMinutes(); // getMinutes方法返回 Date 对象的分钟 (0 ~ 59)
const s = time.getSeconds(); // getSeconds方法返回 Date 对象的秒数 (0 ~ 59)
return y + "-" + M + "-" + d + " " + h + ":" + m + ":" + s;
} else {
return "";
}
}
export function randomStr(length) {
let result = "";
const characters =

View File

@ -3786,6 +3786,11 @@ data-urls@^1.0.0, data-urls@^1.1.0:
whatwg-mimetype "^2.2.0"
whatwg-url "^7.0.0"
dayjs@^1.10.4:
version "1.10.4"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2"
integrity sha512-RI/Hh4kqRc1UKLOAf/T5zdMMX5DQIlDxwUe3wSyMMnEbGunnpENCdbUgM+dW7kXidZqCttBrmw7BhN4TMddkCw==
debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@ -7435,11 +7440,6 @@ mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
moment@^2.29.1:
version "2.29.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
monaco-editor-webpack-plugin@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-3.0.1.tgz#b9bf93314eb2708907c232cfbf359622e7a6d98a"