mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-26 04:02:47 +00:00
Feat: support set custom timezone
This commit is contained in:
parent
522a75c750
commit
08a301d53a
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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={"删除"}>
|
||||
|
|
|
|||
|
|
@ -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={"删除"}>
|
||||
|
|
|
|||
|
|
@ -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={"删除"}>
|
||||
|
|
|
|||
|
|
@ -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={"删除"}>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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)}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 =
|
||||
|
|
|
|||
10
yarn.lock
10
yarn.lock
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue