feat: 修改样式

topwar
guofei 2024-06-03 15:11:15 +08:00
parent a0a9f49548
commit 19c5d15843
4 changed files with 468 additions and 390 deletions

View File

@ -1,100 +1,117 @@
.App {
text-align: center;
display: flex;
align-items: stretch;
text-align: center;
display: flex;
align-items: stretch;
}
.App-logo {
height: 40vmin;
pointer-events: none;
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.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;
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: #61dafb;
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.MuiFormControlLabel-label {
font-size: 13px !important;
font-size: 13px !important;
}
.MuiDialog-root {
background-color: white;
background-color: white;
}
.pane-btn {
border-radius: 4px;
border: 0;
display: inline-block;
height: 30px;
width: 150px;
cursor: pointer;
outline: none;
font-weight: bold;
border-radius: 4px;
border: 0;
display: inline-block;
height: 30px;
line-height: 30px;
width: 150px;
cursor: pointer;
outline: none;
font-weight: bold;
}
.pane-btn.checked {
background-color: #02b564;
color: white;
background-color: #02b564;
color: white;
}
.pane-btn + .pane-btn {
margin-left: 10px;
margin-left: 10px;
}
.generate-btn {
border-radius: 4px;
display: inline-block;
height: 40px;
width: 150px;
cursor: pointer;
outline: none;
font-weight: bold;
font-size: 22px;
background-color: rgb(82, 146, 242);
border: 1px #367fed solid;
border-radius: 4px;
display: inline-block;
height: 40px;
width: 150px;
cursor: pointer;
outline: none;
font-weight: bold;
font-size: 22px;
background-color: rgb(82, 146, 242);
border: 1px #367fed solid;
}
.language-select-container {
display: flex;
align-items: center;
position: absolute;
right: 40px;
top: 30px;
z-index: 9999;
display: flex;
align-items: center;
position: absolute;
right: 40px;
top: 30px;
z-index: 9999;
}
.language-select-container .language-select {
margin-left: 10px;
width: 120px;
height: 30px;
margin-left: 10px;
width: 135px;
height: 35px;
background-color: #eef1f6;
color: #333333;
font-size: 14px;
display: flex;
align-items: center;
}
.language-select-warpper {
position: relative;
}
.language-select-warpper .language-icon {
position: absolute;
left: 18px;
top: 8px;
z-index: 2000;
}
.language-select-container .language-select fieldset {
display: none;
display: none;
}

View File

@ -8,7 +8,12 @@ import waterMarkPng from "./images/water-mark.png";
import React from "react";
import SettingFrame from "./Components/settingsFrame";
import QrCode from "qrcode";
import { getLoginInfo, getProjectSettingValue, setLoginInfo, setProjectSetting } from "./storage";
import {
getLoginInfo,
getProjectSettingValue,
setLoginInfo,
setProjectSetting,
} from "./storage";
import Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem";
import Button from "@mui/material/Button";
@ -22,270 +27,311 @@ import FormControlLabel from "@mui/material/FormControlLabel";
import queryString from "query-string";
import { withTranslation } from "react-i18next";
import i18n from "i18next";
import languageImg from "./images/language.png";
class App extends React.Component {
constructor(props) {
super();
this.state = {
mode: "normal",
projects: [],
selectedProject: {},
selectedProjectName: "",
device: "android-h",
htmlUrl: "",
dataUrl: "",
loading: false,
loginOpen: false,
loginErrorMessage: "",
hideLogo: false,
loginForm: {
account: "",
password: "",
keepLogin: false,
},
language: localStorage.getItem("lang") ?? "zh",
};
this.t = props.t;
}
componentDidMount() {
const loginInfo = getLoginInfo();
var tenant = utils.getTenant();
const hideLogo = queryString.parseUrl(window.location.href)?.query?.hideLogo;
this.setState({ hideLogo: !!hideLogo });
if (loginInfo && loginInfo.permissionPages.includes(tenant)) {
this.fetchData();
this.refreshQrCode();
} else {
this.setState({ loginOpen: true });
loginInfo && this.setState({ loginErrorMessage: "noAuth" });
}
}
constructor(props) {
super();
this.state = {
mode: "normal",
projects: [],
selectedProject: {},
selectedProjectName: "",
device: "android-h",
htmlUrl: "",
dataUrl: "",
loading: false,
loginOpen: false,
loginErrorMessage: "",
hideLogo: false,
loginForm: {
account: "",
password: "",
keepLogin: false,
},
language: localStorage.getItem("lang") ?? "zh",
};
this.t = props.t;
}
componentDidMount() {
const loginInfo = getLoginInfo();
var tenant = utils.getTenant();
const hideLogo = queryString.parseUrl(window.location.href)?.query
?.hideLogo;
this.setState({ hideLogo: !!hideLogo });
if (loginInfo && loginInfo.permissionPages.includes(tenant)) {
this.fetchData();
this.refreshQrCode();
} else {
this.setState({ loginOpen: true });
loginInfo && this.setState({ loginErrorMessage: "noAuth" });
}
}
login = async () => {
var { account, password, keepLogin } = this.state.loginForm;
var user = await LoginApi.UserLogin({ account, password });
var tenant = utils.getTenant();
login = async () => {
var { account, password, keepLogin } = this.state.loginForm;
var user = await LoginApi.UserLogin({ account, password });
var tenant = utils.getTenant();
if (user && user.permissionPages.includes(tenant)) {
this.setState({
loginOpen: false,
});
if (keepLogin) {
setLoginInfo(user);
}
this.fetchData();
this.refreshQrCode();
} else {
this.setState({
loginErrorMessage: user ? "noAuth" : "loginError",
});
}
};
if (user && user.permissionPages.includes(tenant)) {
this.setState({
loginOpen: false,
});
if (keepLogin) {
setLoginInfo(user);
}
this.fetchData();
this.refreshQrCode();
} else {
this.setState({
loginErrorMessage: user ? "noAuth" : "loginError",
});
}
};
refreshQrCode() {
var { selectedProject } = this.state;
selectedProject.HtmlUrl &&
QrCode.toDataURL([{ data: this.selectedProjectUrl, mode: "byte" }], {
width: 120,
}).then((dataUrl) => {
this.setState({
dataUrl: dataUrl,
});
});
}
refreshQrCode() {
var { selectedProject } = this.state;
selectedProject.HtmlUrl &&
QrCode.toDataURL([{ data: this.selectedProjectUrl, mode: "byte" }], {
width: 120,
}).then((dataUrl) => {
this.setState({
dataUrl: dataUrl,
});
});
}
get selectedProjectUrl() {
var { selectedProject } = this.state;
var settingValue = getProjectSettingValue(selectedProject?.Name) || [1, 1, 1];
var rawUrl = selectedProject?.HtmlUrl ? encodeURI(selectedProject?.HtmlUrl) : "";
rawUrl = rawUrl.replace("http:", () => "https:")
if (rawUrl) {
rawUrl += `?datanumber=${settingValue[0]}&datanumber1=${settingValue[1]}&datanumber2=${settingValue[2]}&lunaOrHtml=false`;
}
get selectedProjectUrl() {
var { selectedProject } = this.state;
var settingValue = getProjectSettingValue(selectedProject?.Name) || [
1, 1, 1,
];
var rawUrl = selectedProject?.HtmlUrl
? encodeURI(selectedProject?.HtmlUrl)
: "";
rawUrl = rawUrl.replace("http:", () => "https:");
if (rawUrl) {
rawUrl += `?datanumber=${settingValue[0]}&datanumber1=${settingValue[1]}&datanumber2=${settingValue[2]}&lunaOrHtml=false`;
}
return rawUrl;
}
return rawUrl;
}
async setSelectedProject(selectedProjectName) {
this.setState({
selectedProjectName: selectedProjectName,
});
console.log("selected:", selectedProjectName);
await this.updateSelectedProject(selectedProjectName);
}
async setSelectedProject(selectedProjectName) {
this.setState({
selectedProjectName: selectedProjectName,
});
console.log("selected:", selectedProjectName);
await this.updateSelectedProject(selectedProjectName);
}
async updateSelectedProject(projectName) {
var projectSetting = await ProjectApi.getProjectSetting(projectName);
if (projectSetting.TextObjStr) {
const none = `${this.t("none")}|${this.t("none")}|${this.t("none")}`;
if (projectSetting.TextObjStr.CentText === "无|无|无") {
projectSetting.TextObjStr.CentText = none;
}
if (projectSetting.TextObjStr.MiddText === "无|无|无") {
projectSetting.TextObjStr.MiddText = none;
}
if (projectSetting.TextObjStr.TopText === "无|无|无") {
projectSetting.TextObjStr.TopText = none;
}
}
this.setState({
selectedProject: projectSetting,
});
}
async updateSelectedProject(projectName) {
var projectSetting = await ProjectApi.getProjectSetting(projectName);
if (projectSetting.TextObjStr) {
const none = `${this.t("none")}|${this.t("none")}|${this.t("none")}`;
if (projectSetting.TextObjStr.CentText === "无|无|无") {
projectSetting.TextObjStr.CentText = none;
}
if (projectSetting.TextObjStr.MiddText === "无|无|无") {
projectSetting.TextObjStr.MiddText = none;
}
if (projectSetting.TextObjStr.TopText === "无|无|无") {
projectSetting.TextObjStr.TopText = none;
}
}
this.setState({
selectedProject: projectSetting,
});
}
setDevice(selected) {
this.setState({
device: selected,
});
}
setDevice(selected) {
this.setState({
device: selected,
});
}
setMode(mode) {
this.setState({
mode: mode,
});
}
setMode(mode) {
this.setState({
mode: mode,
});
}
setLoginForm(form) {
this.setState({
loginForm: {
...this.state.loginForm,
...form,
},
});
}
setLoginForm(form) {
this.setState({
loginForm: {
...this.state.loginForm,
...form,
},
});
}
async fetchData() {
this.setState({
loading: true,
});
async fetchData() {
this.setState({
loading: true,
});
var projectNames = (await this.fetchProjects()) || ["无项目"];
this.setState({
projects: projectNames,
selectedProjectName: projectNames[0],
loading: false,
});
await this.updateSelectedProject(projectNames[0]);
this.refreshQrCode();
}
var projectNames = (await this.fetchProjects()) || ["无项目"];
this.setState({
projects: projectNames,
selectedProjectName: projectNames[0],
loading: false,
});
await this.updateSelectedProject(projectNames[0]);
this.refreshQrCode();
}
async fetchProjects() {
return await ProjectApi.getProjects();
}
async fetchProjects() {
return await ProjectApi.getProjects();
}
get projectSettingValue() {
return getProjectSettingValue(this.state.selectedProjectName);
}
get projectSettingValue() {
return getProjectSettingValue(this.state.selectedProjectName);
}
UpdateSetting(obj) {
setProjectSetting(this.state.selectedProject.Name, [+obj.topType, +obj.centreType, +obj.middleType]);
}
UpdateSetting(obj) {
setProjectSetting(this.state.selectedProject.Name, [
+obj.topType,
+obj.centreType,
+obj.middleType,
]);
}
render() {
var { mode, device, projects, selectedProject, dataUrl, loading, loginOpen, loginForm, hideLogo, language } = this.state;
const { t } = this.props;
render() {
var {
mode,
device,
projects,
selectedProject,
dataUrl,
loading,
loginOpen,
loginForm,
hideLogo,
language,
} = this.state;
const { t } = this.props;
return (
<div className="App">
<Dialog open={loginOpen}>
<DialogTitle>{t("login")}</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
id="name"
label={t("account")}
fullWidth
value={loginForm.account}
variant="standard"
onChange={(e) => this.setLoginForm({ account: e.target.value })}
onKeyDown={(e) => e.keyCode === 13 && this.login()}
/>
<TextField
autoFocus
margin="dense"
id="name"
label={t("password")}
type={"password"}
fullWidth
value={loginForm.password}
variant="standard"
onChange={(e) => this.setLoginForm({ password: e.target.value })}
onKeyDown={(e) => e.keyCode === 13 && this.login()}
/>
</DialogContent>
<DialogActions>
{this.state.loginErrorMessage && (
<span
style={{
color: "red",
fontSize: "12px",
margin: "0 auto 0 10px",
}}>
{t(this.state.loginErrorMessage)}
</span>
)}
<FormControlLabel
control={<Checkbox checked={loginForm.keepLogin} onChange={(_, checked) => this.setLoginForm({ keepLogin: checked })} />}
label={t("keepLoginStatus")}
/>
<Button onClick={() => this.login()}>{t("submit")}</Button>
</DialogActions>
</Dialog>
<Pane
mode={mode}
onModeChange={(_mode) => this.setMode(_mode)}
projects={projects}
device={device}
loading={loading}
hideLogo={hideLogo}
onProjectSelect={(project) => {
this.setSelectedProject(project);
}}
onDeviceChange={(_device) => this.setDevice(_device)}
/>
<div className="language-select-container">
<div>{t("language")}:</div>
<Select
className="language-select"
value={language}
label={t("language")}
sx={{
background: "rgb(115, 158, 211)",
width: "80%",
color: "white",
}}
onChange={(e) => {
let lang = e.target.value;
this.setState({
language: lang,
});
i18n.changeLanguage(lang);
localStorage.setItem("lang", lang);
}}>
<MenuItem value="en">English</MenuItem>
<MenuItem value="zh">Chinese</MenuItem>
</Select>
</div>
<div
style={{
display: "flex",
flex: 1,
alignItems: "center",
backgroundImage: hideLogo ? "" : `url(${waterMarkPng})`,
backgroundPositionX: "right",
backgroundPositionY: "bottom",
backgroundRepeat: "no-repeat",
}}>
{mode === "normal" ? (
<DeviceFrame htmlUrl={this.selectedProjectUrl} device={device} qrDataUrl={dataUrl} refreshQrCode={() => this.refreshQrCode()} />
) : (
<SettingFrame setting={selectedProject?.TextObjStr} settingValue={this.projectSettingValue} generate={(value) => this.UpdateSetting(value)} />
)}
</div>
</div>
);
}
return (
<div className="App">
<Dialog open={loginOpen}>
<DialogTitle>{t("login")}</DialogTitle>
<DialogContent>
<TextField
autoFocus
margin="dense"
id="name"
label={t("account")}
fullWidth
value={loginForm.account}
variant="standard"
onChange={(e) => this.setLoginForm({ account: e.target.value })}
onKeyDown={(e) => e.keyCode === 13 && this.login()}
/>
<TextField
autoFocus
margin="dense"
id="name"
label={t("password")}
type={"password"}
fullWidth
value={loginForm.password}
variant="standard"
onChange={(e) => this.setLoginForm({ password: e.target.value })}
onKeyDown={(e) => e.keyCode === 13 && this.login()}
/>
</DialogContent>
<DialogActions>
{this.state.loginErrorMessage && (
<span
style={{
color: "red",
fontSize: "12px",
margin: "0 auto 0 10px",
}}
>
{t(this.state.loginErrorMessage)}
</span>
)}
<FormControlLabel
control={
<Checkbox
checked={loginForm.keepLogin}
onChange={(_, checked) =>
this.setLoginForm({ keepLogin: checked })
}
/>
}
label={t("keepLoginStatus")}
/>
<Button onClick={() => this.login()}>{t("submit")}</Button>
</DialogActions>
</Dialog>
<Pane
mode={mode}
onModeChange={(_mode) => this.setMode(_mode)}
projects={projects}
device={device}
loading={loading}
hideLogo={hideLogo}
onProjectSelect={(project) => {
this.setSelectedProject(project);
}}
onDeviceChange={(_device) => this.setDevice(_device)}
/>
<div className="language-select-container">
<div className="language-select-warpper">
<img className="language-icon" src={languageImg} style={{ width: "18px" }} alt="" />
<Select
className="language-select"
value={language}
label={t("language")}
onChange={(e) => {
let lang = e.target.value;
this.setState({
language: lang,
});
i18n.changeLanguage(lang);
localStorage.setItem("lang", lang);
}}
>
<MenuItem style={{ fontSize: "14px" }} value="en">
English
</MenuItem>
<MenuItem style={{ fontSize: "14px" }} value="zh">
简体中文
</MenuItem>
</Select>
</div>
</div>
<div
style={{
display: "flex",
flex: 1,
alignItems: "center",
backgroundImage: hideLogo ? "" : `url(${waterMarkPng})`,
backgroundPositionX: "right",
backgroundPositionY: "bottom",
backgroundRepeat: "no-repeat",
}}
>
{mode === "normal" ? (
<DeviceFrame
htmlUrl={this.selectedProjectUrl}
device={device}
qrDataUrl={dataUrl}
refreshQrCode={() => this.refreshQrCode()}
/>
) : (
<SettingFrame
setting={selectedProject?.TextObjStr}
settingValue={this.projectSettingValue}
generate={(value) => this.UpdateSetting(value)}
/>
)}
</div>
</div>
);
}
}
export default withTranslation()(App);

View File

@ -2,102 +2,117 @@ import { Alert, Grid, List, ListItem, Snackbar } from "@mui/material";
import { useEffect, useState } from "react";
import "./index.css";
import checkPng from "../../images/check.png";
import generateButtonPng from "../../images/generate-button.png";
import { useTranslation } from "react-i18next";
var rows = ["TopText", "CentText", "MiddText"];
var rowTitles = ["start", "middle", "end"];
function SettingFrame(props) {
var [settingArr, setSettingArr] = useState([1, 2, 3]);
var [openPopup, setOpenPopup] = useState(false);
const { t } = useTranslation();
var [settingArr, setSettingArr] = useState([1, 2, 3]);
var [openPopup, setOpenPopup] = useState(false);
const { t } = useTranslation();
useEffect(() => {
setSettingArr(props.settingValue);
}, [props.settingValue]);
useEffect(() => {
setSettingArr(props.settingValue);
}, [props.settingValue]);
return (
<div
style={{
flex: 1,
}}>
<Snackbar
open={openPopup}
onClose={() => setOpenPopup(false)}
autoHideDuration={3000}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}>
<Alert severity="success" sx={{ width: "100%" }}>
{t("saveSuccess")}
</Alert>
</Snackbar>
<List>
{rows.map((rowKey, rowIndex) => (
<ListItem key={rowIndex}>
<span>{t(rowTitles[rowIndex])}</span>
<Grid container spacing={{ xs: 2, md: 3 }} columns={{ xs: 4, sm: 8, md: 12 }}>
{(props.setting && props.setting[rowKey] ? Array.from(t(props.setting[rowKey]).split("|")) : [t("none"), t("none"), t("none")]).map((text, index) => (
<Grid item xs={2} sm={4} md={4} key={index}>
<div
className="setting-card"
style={{
position: "relative",
height: "120px",
textAlign: "center",
justifyContent: "center",
display: "flex",
alignItems: "center",
cursor: "pointer",
}}
onClick={() => {
var newSetting = settingArr.slice();
newSetting[rowIndex] = index + 1;
setSettingArr(newSetting);
}}>
{text}
<img
src={checkPng}
alt="check"
style={{
position: "absolute",
right: "10px",
top: "10px",
display: index + 1 === settingArr[rowIndex] ? "block" : "none",
}}
/>
</div>
</Grid>
))}
</Grid>
</ListItem>
))}
</List>
<div
style={{
alignItems: "center",
cursor: "pointer",
marginTop: "30px",
}}>
{props.setting && (
<button
className="generate-btn"
onClick={async () => {
await props.generate({
topType: settingArr[0] + "",
centreType: settingArr[1] + "",
middleType: settingArr[2] + "",
});
return (
<div
style={{
flex: 1,
}}
>
<Snackbar
open={openPopup}
onClose={() => setOpenPopup(false)}
autoHideDuration={3000}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<Alert severity="success" sx={{ width: "100%" }}>
{t("saveSuccess")}
</Alert>
</Snackbar>
<List style={{ width: "80%", margin: "auto" }}>
{rows.map((rowKey, rowIndex) => (
<ListItem key={rowIndex}>
<span style={{ display: "inline-block", width: "40px" }}>
{t(rowTitles[rowIndex])}
</span>
<Grid
container
spacing={{ xs: 2, md: 3 }}
columns={{ xs: 4, sm: 8, md: 12 }}
>
{(props.setting && props.setting[rowKey]
? Array.from(t(props.setting[rowKey]).split("|"))
: [t("none"), t("none"), t("none")]
).map((text, index) => (
<Grid item xs={4} sm={4} md={4} key={index}>
<div
className="setting-card"
style={{
position: "relative",
height: "120px",
textAlign: "center",
justifyContent: "center",
display: "flex",
alignItems: "center",
cursor: "pointer",
}}
onClick={() => {
var newSetting = settingArr.slice();
newSetting[rowIndex] = index + 1;
setSettingArr(newSetting);
}}
>
{text}
<img
src={checkPng}
alt="check"
style={{
position: "absolute",
right: "10px",
top: "10px",
display:
index + 1 === settingArr[rowIndex] ? "block" : "none",
}}
/>
</div>
</Grid>
))}
</Grid>
</ListItem>
))}
</List>
<div
style={{
width: "80%",
alignItems: "center",
cursor: "pointer",
margin: "30px auto 0",
}}
>
{props.setting && (
<button
className="generate-btn"
onClick={async () => {
await props.generate({
topType: settingArr[0] + "",
centreType: settingArr[1] + "",
middleType: settingArr[2] + "",
});
setOpenPopup(true);
}}>
{t("generate")}
</button>
)}
</div>
</div>
);
setOpenPopup(true);
}}
>
{t("generate")}
</button>
)}
</div>
</div>
);
}
export default SettingFrame;

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB