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 { .App {
text-align: center; text-align: center;
display: flex; display: flex;
align-items: stretch; align-items: stretch;
} }
.App-logo { .App-logo {
height: 40vmin; height: 40vmin;
pointer-events: none; pointer-events: none;
} }
@media (prefers-reduced-motion: no-preference) { @media (prefers-reduced-motion: no-preference) {
.App-logo { .App-logo {
animation: App-logo-spin infinite 20s linear; animation: App-logo-spin infinite 20s linear;
} }
} }
.App-header { .App-header {
background-color: #282c34; background-color: #282c34;
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-size: calc(10px + 2vmin); font-size: calc(10px + 2vmin);
color: white; color: white;
} }
.App-link { .App-link {
color: #61dafb; color: #61dafb;
} }
@keyframes App-logo-spin { @keyframes App-logo-spin {
from { from {
transform: rotate(0deg); transform: rotate(0deg);
} }
to { to {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.MuiFormControlLabel-label { .MuiFormControlLabel-label {
font-size: 13px !important; font-size: 13px !important;
} }
.MuiDialog-root { .MuiDialog-root {
background-color: white; background-color: white;
} }
.pane-btn { .pane-btn {
border-radius: 4px; border-radius: 4px;
border: 0; border: 0;
display: inline-block; display: inline-block;
height: 30px; height: 30px;
width: 150px; line-height: 30px;
cursor: pointer; width: 150px;
outline: none; cursor: pointer;
font-weight: bold; outline: none;
font-weight: bold;
} }
.pane-btn.checked { .pane-btn.checked {
background-color: #02b564; background-color: #02b564;
color: white; color: white;
} }
.pane-btn + .pane-btn { .pane-btn + .pane-btn {
margin-left: 10px; margin-left: 10px;
} }
.generate-btn { .generate-btn {
border-radius: 4px; border-radius: 4px;
display: inline-block; display: inline-block;
height: 40px; height: 40px;
width: 150px; width: 150px;
cursor: pointer; cursor: pointer;
outline: none; outline: none;
font-weight: bold; font-weight: bold;
font-size: 22px; font-size: 22px;
background-color: rgb(82, 146, 242); background-color: rgb(82, 146, 242);
border: 1px #367fed solid; border: 1px #367fed solid;
} }
.language-select-container { .language-select-container {
display: flex; display: flex;
align-items: center; align-items: center;
position: absolute; position: absolute;
right: 40px; right: 40px;
top: 30px; top: 30px;
z-index: 9999; z-index: 9999;
} }
.language-select-container .language-select { .language-select-container .language-select {
margin-left: 10px; margin-left: 10px;
width: 120px; width: 135px;
height: 30px; 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 { .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 React from "react";
import SettingFrame from "./Components/settingsFrame"; import SettingFrame from "./Components/settingsFrame";
import QrCode from "qrcode"; 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 Select from "@mui/material/Select";
import MenuItem from "@mui/material/MenuItem"; import MenuItem from "@mui/material/MenuItem";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
@ -22,270 +27,311 @@ import FormControlLabel from "@mui/material/FormControlLabel";
import queryString from "query-string"; import queryString from "query-string";
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import i18n from "i18next"; import i18n from "i18next";
import languageImg from "./images/language.png";
class App extends React.Component { class App extends React.Component {
constructor(props) { constructor(props) {
super(); super();
this.state = { this.state = {
mode: "normal", mode: "normal",
projects: [], projects: [],
selectedProject: {}, selectedProject: {},
selectedProjectName: "", selectedProjectName: "",
device: "android-h", device: "android-h",
htmlUrl: "", htmlUrl: "",
dataUrl: "", dataUrl: "",
loading: false, loading: false,
loginOpen: false, loginOpen: false,
loginErrorMessage: "", loginErrorMessage: "",
hideLogo: false, hideLogo: false,
loginForm: { loginForm: {
account: "", account: "",
password: "", password: "",
keepLogin: false, keepLogin: false,
}, },
language: localStorage.getItem("lang") ?? "zh", language: localStorage.getItem("lang") ?? "zh",
}; };
this.t = props.t; this.t = props.t;
} }
componentDidMount() { componentDidMount() {
const loginInfo = getLoginInfo(); const loginInfo = getLoginInfo();
var tenant = utils.getTenant(); var tenant = utils.getTenant();
const hideLogo = queryString.parseUrl(window.location.href)?.query?.hideLogo; const hideLogo = queryString.parseUrl(window.location.href)?.query
this.setState({ hideLogo: !!hideLogo }); ?.hideLogo;
if (loginInfo && loginInfo.permissionPages.includes(tenant)) { this.setState({ hideLogo: !!hideLogo });
this.fetchData(); if (loginInfo && loginInfo.permissionPages.includes(tenant)) {
this.refreshQrCode(); this.fetchData();
} else { this.refreshQrCode();
this.setState({ loginOpen: true }); } else {
loginInfo && this.setState({ loginErrorMessage: "noAuth" }); this.setState({ loginOpen: true });
} loginInfo && this.setState({ loginErrorMessage: "noAuth" });
} }
}
login = async () => { login = async () => {
var { account, password, keepLogin } = this.state.loginForm; var { account, password, keepLogin } = this.state.loginForm;
var user = await LoginApi.UserLogin({ account, password }); var user = await LoginApi.UserLogin({ account, password });
var tenant = utils.getTenant(); var tenant = utils.getTenant();
if (user && user.permissionPages.includes(tenant)) { if (user && user.permissionPages.includes(tenant)) {
this.setState({ this.setState({
loginOpen: false, loginOpen: false,
}); });
if (keepLogin) { if (keepLogin) {
setLoginInfo(user); setLoginInfo(user);
} }
this.fetchData(); this.fetchData();
this.refreshQrCode(); this.refreshQrCode();
} else { } else {
this.setState({ this.setState({
loginErrorMessage: user ? "noAuth" : "loginError", loginErrorMessage: user ? "noAuth" : "loginError",
}); });
} }
}; };
refreshQrCode() { refreshQrCode() {
var { selectedProject } = this.state; var { selectedProject } = this.state;
selectedProject.HtmlUrl && selectedProject.HtmlUrl &&
QrCode.toDataURL([{ data: this.selectedProjectUrl, mode: "byte" }], { QrCode.toDataURL([{ data: this.selectedProjectUrl, mode: "byte" }], {
width: 120, width: 120,
}).then((dataUrl) => { }).then((dataUrl) => {
this.setState({ this.setState({
dataUrl: dataUrl, dataUrl: dataUrl,
}); });
}); });
} }
get selectedProjectUrl() { get selectedProjectUrl() {
var { selectedProject } = this.state; var { selectedProject } = this.state;
var settingValue = getProjectSettingValue(selectedProject?.Name) || [1, 1, 1]; var settingValue = getProjectSettingValue(selectedProject?.Name) || [
var rawUrl = selectedProject?.HtmlUrl ? encodeURI(selectedProject?.HtmlUrl) : ""; 1, 1, 1,
rawUrl = rawUrl.replace("http:", () => "https:") ];
if (rawUrl) { var rawUrl = selectedProject?.HtmlUrl
rawUrl += `?datanumber=${settingValue[0]}&datanumber1=${settingValue[1]}&datanumber2=${settingValue[2]}&lunaOrHtml=false`; ? 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) { async setSelectedProject(selectedProjectName) {
this.setState({ this.setState({
selectedProjectName: selectedProjectName, selectedProjectName: selectedProjectName,
}); });
console.log("selected:", selectedProjectName); console.log("selected:", selectedProjectName);
await this.updateSelectedProject(selectedProjectName); await this.updateSelectedProject(selectedProjectName);
} }
async updateSelectedProject(projectName) { async updateSelectedProject(projectName) {
var projectSetting = await ProjectApi.getProjectSetting(projectName); var projectSetting = await ProjectApi.getProjectSetting(projectName);
if (projectSetting.TextObjStr) { if (projectSetting.TextObjStr) {
const none = `${this.t("none")}|${this.t("none")}|${this.t("none")}`; const none = `${this.t("none")}|${this.t("none")}|${this.t("none")}`;
if (projectSetting.TextObjStr.CentText === "无|无|无") { if (projectSetting.TextObjStr.CentText === "无|无|无") {
projectSetting.TextObjStr.CentText = none; projectSetting.TextObjStr.CentText = none;
} }
if (projectSetting.TextObjStr.MiddText === "无|无|无") { if (projectSetting.TextObjStr.MiddText === "无|无|无") {
projectSetting.TextObjStr.MiddText = none; projectSetting.TextObjStr.MiddText = none;
} }
if (projectSetting.TextObjStr.TopText === "无|无|无") { if (projectSetting.TextObjStr.TopText === "无|无|无") {
projectSetting.TextObjStr.TopText = none; projectSetting.TextObjStr.TopText = none;
} }
} }
this.setState({ this.setState({
selectedProject: projectSetting, selectedProject: projectSetting,
}); });
} }
setDevice(selected) { setDevice(selected) {
this.setState({ this.setState({
device: selected, device: selected,
}); });
} }
setMode(mode) { setMode(mode) {
this.setState({ this.setState({
mode: mode, mode: mode,
}); });
} }
setLoginForm(form) { setLoginForm(form) {
this.setState({ this.setState({
loginForm: { loginForm: {
...this.state.loginForm, ...this.state.loginForm,
...form, ...form,
}, },
}); });
} }
async fetchData() { async fetchData() {
this.setState({ this.setState({
loading: true, loading: true,
}); });
var projectNames = (await this.fetchProjects()) || ["无项目"]; var projectNames = (await this.fetchProjects()) || ["无项目"];
this.setState({ this.setState({
projects: projectNames, projects: projectNames,
selectedProjectName: projectNames[0], selectedProjectName: projectNames[0],
loading: false, loading: false,
}); });
await this.updateSelectedProject(projectNames[0]); await this.updateSelectedProject(projectNames[0]);
this.refreshQrCode(); this.refreshQrCode();
} }
async fetchProjects() { async fetchProjects() {
return await ProjectApi.getProjects(); return await ProjectApi.getProjects();
} }
get projectSettingValue() { get projectSettingValue() {
return getProjectSettingValue(this.state.selectedProjectName); return getProjectSettingValue(this.state.selectedProjectName);
} }
UpdateSetting(obj) { UpdateSetting(obj) {
setProjectSetting(this.state.selectedProject.Name, [+obj.topType, +obj.centreType, +obj.middleType]); setProjectSetting(this.state.selectedProject.Name, [
} +obj.topType,
+obj.centreType,
+obj.middleType,
]);
}
render() { render() {
var { mode, device, projects, selectedProject, dataUrl, loading, loginOpen, loginForm, hideLogo, language } = this.state; var {
const { t } = this.props; mode,
device,
projects,
selectedProject,
dataUrl,
loading,
loginOpen,
loginForm,
hideLogo,
language,
} = this.state;
const { t } = this.props;
return ( return (
<div className="App"> <div className="App">
<Dialog open={loginOpen}> <Dialog open={loginOpen}>
<DialogTitle>{t("login")}</DialogTitle> <DialogTitle>{t("login")}</DialogTitle>
<DialogContent> <DialogContent>
<TextField <TextField
autoFocus autoFocus
margin="dense" margin="dense"
id="name" id="name"
label={t("account")} label={t("account")}
fullWidth fullWidth
value={loginForm.account} value={loginForm.account}
variant="standard" variant="standard"
onChange={(e) => this.setLoginForm({ account: e.target.value })} onChange={(e) => this.setLoginForm({ account: e.target.value })}
onKeyDown={(e) => e.keyCode === 13 && this.login()} onKeyDown={(e) => e.keyCode === 13 && this.login()}
/> />
<TextField <TextField
autoFocus autoFocus
margin="dense" margin="dense"
id="name" id="name"
label={t("password")} label={t("password")}
type={"password"} type={"password"}
fullWidth fullWidth
value={loginForm.password} value={loginForm.password}
variant="standard" variant="standard"
onChange={(e) => this.setLoginForm({ password: e.target.value })} onChange={(e) => this.setLoginForm({ password: e.target.value })}
onKeyDown={(e) => e.keyCode === 13 && this.login()} onKeyDown={(e) => e.keyCode === 13 && this.login()}
/> />
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
{this.state.loginErrorMessage && ( {this.state.loginErrorMessage && (
<span <span
style={{ style={{
color: "red", color: "red",
fontSize: "12px", fontSize: "12px",
margin: "0 auto 0 10px", margin: "0 auto 0 10px",
}}> }}
{t(this.state.loginErrorMessage)} >
</span> {t(this.state.loginErrorMessage)}
)} </span>
<FormControlLabel )}
control={<Checkbox checked={loginForm.keepLogin} onChange={(_, checked) => this.setLoginForm({ keepLogin: checked })} />} <FormControlLabel
label={t("keepLoginStatus")} control={
/> <Checkbox
<Button onClick={() => this.login()}>{t("submit")}</Button> checked={loginForm.keepLogin}
</DialogActions> onChange={(_, checked) =>
</Dialog> this.setLoginForm({ keepLogin: checked })
<Pane }
mode={mode} />
onModeChange={(_mode) => this.setMode(_mode)} }
projects={projects} label={t("keepLoginStatus")}
device={device} />
loading={loading} <Button onClick={() => this.login()}>{t("submit")}</Button>
hideLogo={hideLogo} </DialogActions>
onProjectSelect={(project) => { </Dialog>
this.setSelectedProject(project); <Pane
}} mode={mode}
onDeviceChange={(_device) => this.setDevice(_device)} onModeChange={(_mode) => this.setMode(_mode)}
/> projects={projects}
<div className="language-select-container"> device={device}
<div>{t("language")}:</div> loading={loading}
<Select hideLogo={hideLogo}
className="language-select" onProjectSelect={(project) => {
value={language} this.setSelectedProject(project);
label={t("language")} }}
sx={{ onDeviceChange={(_device) => this.setDevice(_device)}
background: "rgb(115, 158, 211)", />
width: "80%", <div className="language-select-container">
color: "white", <div className="language-select-warpper">
}} <img className="language-icon" src={languageImg} style={{ width: "18px" }} alt="" />
onChange={(e) => { <Select
let lang = e.target.value; className="language-select"
this.setState({ value={language}
language: lang, label={t("language")}
}); onChange={(e) => {
i18n.changeLanguage(lang); let lang = e.target.value;
localStorage.setItem("lang", lang); this.setState({
}}> language: lang,
<MenuItem value="en">English</MenuItem> });
<MenuItem value="zh">Chinese</MenuItem> i18n.changeLanguage(lang);
</Select> localStorage.setItem("lang", lang);
</div> }}
<div >
style={{ <MenuItem style={{ fontSize: "14px" }} value="en">
display: "flex", English
flex: 1, </MenuItem>
alignItems: "center", <MenuItem style={{ fontSize: "14px" }} value="zh">
backgroundImage: hideLogo ? "" : `url(${waterMarkPng})`, 简体中文
backgroundPositionX: "right", </MenuItem>
backgroundPositionY: "bottom", </Select>
backgroundRepeat: "no-repeat", </div>
}}> </div>
{mode === "normal" ? ( <div
<DeviceFrame htmlUrl={this.selectedProjectUrl} device={device} qrDataUrl={dataUrl} refreshQrCode={() => this.refreshQrCode()} /> style={{
) : ( display: "flex",
<SettingFrame setting={selectedProject?.TextObjStr} settingValue={this.projectSettingValue} generate={(value) => this.UpdateSetting(value)} /> flex: 1,
)} alignItems: "center",
</div> backgroundImage: hideLogo ? "" : `url(${waterMarkPng})`,
</div> 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); 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 { useEffect, useState } from "react";
import "./index.css"; import "./index.css";
import checkPng from "../../images/check.png"; import checkPng from "../../images/check.png";
import generateButtonPng from "../../images/generate-button.png";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
var rows = ["TopText", "CentText", "MiddText"]; var rows = ["TopText", "CentText", "MiddText"];
var rowTitles = ["start", "middle", "end"]; var rowTitles = ["start", "middle", "end"];
function SettingFrame(props) { function SettingFrame(props) {
var [settingArr, setSettingArr] = useState([1, 2, 3]); var [settingArr, setSettingArr] = useState([1, 2, 3]);
var [openPopup, setOpenPopup] = useState(false); var [openPopup, setOpenPopup] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => { useEffect(() => {
setSettingArr(props.settingValue); setSettingArr(props.settingValue);
}, [props.settingValue]); }, [props.settingValue]);
return ( return (
<div <div
style={{ style={{
flex: 1, flex: 1,
}}> }}
<Snackbar >
open={openPopup} <Snackbar
onClose={() => setOpenPopup(false)} open={openPopup}
autoHideDuration={3000} onClose={() => setOpenPopup(false)}
anchorOrigin={{ autoHideDuration={3000}
vertical: "top", anchorOrigin={{
horizontal: "right", vertical: "top",
}}> horizontal: "right",
<Alert severity="success" sx={{ width: "100%" }}> }}
{t("saveSuccess")} >
</Alert> <Alert severity="success" sx={{ width: "100%" }}>
</Snackbar> {t("saveSuccess")}
<List> </Alert>
{rows.map((rowKey, rowIndex) => ( </Snackbar>
<ListItem key={rowIndex}> <List style={{ width: "80%", margin: "auto" }}>
<span>{t(rowTitles[rowIndex])}</span> {rows.map((rowKey, rowIndex) => (
<Grid container spacing={{ xs: 2, md: 3 }} columns={{ xs: 4, sm: 8, md: 12 }}> <ListItem key={rowIndex}>
{(props.setting && props.setting[rowKey] ? Array.from(t(props.setting[rowKey]).split("|")) : [t("none"), t("none"), t("none")]).map((text, index) => ( <span style={{ display: "inline-block", width: "40px" }}>
<Grid item xs={2} sm={4} md={4} key={index}> {t(rowTitles[rowIndex])}
<div </span>
className="setting-card" <Grid
style={{ container
position: "relative", spacing={{ xs: 2, md: 3 }}
height: "120px", columns={{ xs: 4, sm: 8, md: 12 }}
textAlign: "center", >
justifyContent: "center", {(props.setting && props.setting[rowKey]
display: "flex", ? Array.from(t(props.setting[rowKey]).split("|"))
alignItems: "center", : [t("none"), t("none"), t("none")]
cursor: "pointer", ).map((text, index) => (
}} <Grid item xs={4} sm={4} md={4} key={index}>
onClick={() => { <div
var newSetting = settingArr.slice(); className="setting-card"
newSetting[rowIndex] = index + 1; style={{
setSettingArr(newSetting); position: "relative",
}}> height: "120px",
{text} textAlign: "center",
<img justifyContent: "center",
src={checkPng} display: "flex",
alt="check" alignItems: "center",
style={{ cursor: "pointer",
position: "absolute", }}
right: "10px", onClick={() => {
top: "10px", var newSetting = settingArr.slice();
display: index + 1 === settingArr[rowIndex] ? "block" : "none", newSetting[rowIndex] = index + 1;
}} setSettingArr(newSetting);
/> }}
</div> >
</Grid> {text}
))} <img
</Grid> src={checkPng}
</ListItem> alt="check"
))} style={{
</List> position: "absolute",
<div right: "10px",
style={{ top: "10px",
alignItems: "center", display:
cursor: "pointer", index + 1 === settingArr[rowIndex] ? "block" : "none",
marginTop: "30px", }}
}}> />
{props.setting && ( </div>
<button </Grid>
className="generate-btn" ))}
onClick={async () => { </Grid>
await props.generate({ </ListItem>
topType: settingArr[0] + "", ))}
centreType: settingArr[1] + "", </List>
middleType: settingArr[2] + "", <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); setOpenPopup(true);
}}> }}
{t("generate")} >
</button> {t("generate")}
)} </button>
</div> )}
</div> </div>
); </div>
);
} }
export default SettingFrame; export default SettingFrame;

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB