Merge remote-tracking branch 'github/feat/1.1.2/ui' into feat/1.1.2/user-center

This commit is contained in:
LinkinStars 2023-04-18 10:49:44 +08:00
commit 673ebfbb78
19 changed files with 157 additions and 71 deletions

View File

@ -1478,6 +1478,9 @@ ui:
footer: footer:
label: Footer label: Footer
text: This will insert before </html>. text: This will insert before </html>.
sidebar:
label: Sidebar
text: This will insert in sidebar.
login: login:
page_title: Login page_title: Login
membership: membership:

View File

@ -384,6 +384,7 @@ export interface AdminSettingsCustom {
custom_head: string; custom_head: string;
custom_header: string; custom_header: string;
custom_footer: string; custom_footer: string;
custom_sidebar: string;
} }
export interface AdminSettingsLogin { export interface AdminSettingsLogin {

View File

@ -1,6 +1,9 @@
const pattern = { const pattern = {
email: email:
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/, /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$/,
wx: /micromessenger/,
wxwork: /wxwork/,
dingtalk: /dingtalk/,
}; };
export default pattern; export default pattern;

View File

@ -0,0 +1,11 @@
import { memo } from 'react';
import { customizeStore } from '@/stores';
const Index = () => {
const { custom_sidebar } = customizeStore((state) => state);
if (!custom_sidebar) return null;
return <div dangerouslySetInnerHTML={{ __html: custom_sidebar }} />;
};
export default memo(Index);

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
import { usePageTags } from '@/hooks'; import { usePageTags } from '@/hooks';
const Index = ({ httpCode = '', errMsg = '', showErroCode = true }) => { const Index = ({ httpCode = '', errMsg = '', showErrorCode = true }) => {
const { t } = useTranslation('translation', { keyPrefix: 'page_error' }); const { t } = useTranslation('translation', { keyPrefix: 'page_error' });
useEffect(() => { useEffect(() => {
// auto height of container // auto height of container
@ -31,7 +31,7 @@ const Index = ({ httpCode = '', errMsg = '', showErroCode = true }) => {
style={{ fontSize: '120px', lineHeight: 1.2 }}> style={{ fontSize: '120px', lineHeight: 1.2 }}>
(=x=) (=x=)
</div> </div>
{showErroCode && ( {showErrorCode && (
<h4 className="text-center">{t('http_error', { code: httpCode })}</h4> <h4 className="text-center">{t('http_error', { code: httpCode })}</h4>
)} )}
<div className="text-center mb-3 fs-5"> <div className="text-center mb-3 fs-5">

View File

@ -38,6 +38,7 @@ import Counts from './Counts';
import QuestionList from './QuestionList'; import QuestionList from './QuestionList';
import HotQuestions from './HotQuestions'; import HotQuestions from './HotQuestions';
import HttpErrorContent from './HttpErrorContent'; import HttpErrorContent from './HttpErrorContent';
import CustomSidebar from './CustomSidebar';
export { export {
Avatar, Avatar,
@ -82,5 +83,6 @@ export {
QuestionList, QuestionList,
HotQuestions, HotQuestions,
HttpErrorContent, HttpErrorContent,
CustomSidebar,
}; };
export type { EditorRef, JSONSchema, UISchema }; export type { EditorRef, JSONSchema, UISchema };

View File

@ -5,7 +5,7 @@ import { HttpErrorContent } from '@/components';
const Index = () => { const Index = () => {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const msg = searchParams.get('msg') || ''; const msg = searchParams.get('msg') || '';
return <HttpErrorContent httpCode="50X" errMsg={msg} showErroCode={!msg} />; return <HttpErrorContent httpCode="50X" errMsg={msg} showErrorCode={!msg} />;
}; };
export default Index; export default Index;

View File

@ -31,6 +31,11 @@ const Index: FC = () => {
title: t('header.label'), title: t('header.label'),
description: t('header.text'), description: t('header.text'),
}, },
custom_sidebar: {
type: 'string',
title: t('sidebar.label'),
description: t('sidebar.text'),
},
custom_footer: { custom_footer: {
type: 'string', type: 'string',
title: t('footer.label'), title: t('footer.label'),
@ -60,6 +65,13 @@ const Index: FC = () => {
className: ['fs-14', 'font-monospace'], className: ['fs-14', 'font-monospace'],
}, },
}, },
custom_sidebar: {
'ui:widget': 'textarea',
'ui:options': {
rows: 10,
className: ['fs-14', 'font-monospace'],
},
},
custom_footer: { custom_footer: {
'ui:widget': 'textarea', 'ui:widget': 'textarea',
'ui:options': { 'ui:options': {
@ -77,6 +89,7 @@ const Index: FC = () => {
custom_css: formData.custom_css.value, custom_css: formData.custom_css.value,
custom_head: formData.custom_head.value, custom_head: formData.custom_head.value,
custom_header: formData.custom_header.value, custom_header: formData.custom_header.value,
custom_sidebar: formData.custom_sidebar.value,
custom_footer: formData.custom_footer.value, custom_footer: formData.custom_footer.value,
}; };
@ -103,6 +116,7 @@ const Index: FC = () => {
formMeta.custom_css.value = setting.custom_css; formMeta.custom_css.value = setting.custom_css;
formMeta.custom_head.value = setting.custom_head; formMeta.custom_head.value = setting.custom_head;
formMeta.custom_header.value = setting.custom_header; formMeta.custom_header.value = setting.custom_header;
formMeta.custom_sidebar.value = setting.custom_sidebar;
formMeta.custom_footer.value = setting.custom_footer; formMeta.custom_footer.value = setting.custom_footer;
setFormData(formMeta); setFormData(formMeta);
} }

View File

@ -1,57 +0,0 @@
import { memo } from 'react';
import { Container, Card, Col, Carousel } from 'react-bootstrap';
const data = [
{
id: 1,
url: require('@/assets/images/carousel-wecom-1.jpg'),
},
{
id: 2,
url: require('@/assets/images/carousel-wecom-2.jpg'),
},
{
id: 3,
url: require('@/assets/images/carousel-wecom-3.jpg'),
},
{
id: 4,
url: require('@/assets/images/carousel-wecom-4.jpg'),
},
{
id: 5,
url: require('@/assets/images/carousel-wecom-5.jpg'),
},
];
const Index = () => {
return (
<Container>
<Col lg={4} className="mx-auto mt-3 py-5">
<Card>
<Card.Body>
<h3 className="text-center pt-3 mb-3">WeCome Login</h3>
<p className="text-danger text-center">
Login failed, please allow this app to access your email
information before try again.
</p>
<Carousel controls={false}>
{data.map((item) => (
<Carousel.Item key={item.id}>
<img
className="d-block w-100"
src={item.url}
alt="First slide"
/>
</Carousel.Item>
))}
</Carousel>
</Card.Body>
</Card>
</Col>
</Container>
);
};
export default memo(Index);

View File

@ -8,7 +8,7 @@ import {
} from 'react-router-dom'; } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Pagination } from '@/components'; import { Pagination, CustomSidebar } from '@/components';
import { loggedUserInfoStore, toastStore } from '@/stores'; import { loggedUserInfoStore, toastStore } from '@/stores';
import { scrollToElementTop } from '@/utils'; import { scrollToElementTop } from '@/utils';
import { usePageTags, usePageUsers } from '@/hooks'; import { usePageTags, usePageUsers } from '@/hooks';
@ -256,6 +256,7 @@ const Index = () => {
)} )}
</Col> </Col>
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0"> <Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
<CustomSidebar />
<RelatedQuestions id={question?.id || ''} /> <RelatedQuestions id={question?.id || ''} />
</Col> </Col>
</Row> </Row>

View File

@ -4,7 +4,12 @@ import { useMatch, Link, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { usePageTags } from '@/hooks'; import { usePageTags } from '@/hooks';
import { FollowingTags, QuestionList, HotQuestions } from '@/components'; import {
FollowingTags,
QuestionList,
HotQuestions,
CustomSidebar,
} from '@/components';
import { siteInfoStore, loggedUserInfoStore } from '@/stores'; import { siteInfoStore, loggedUserInfoStore } from '@/stores';
import { useQuestionList } from '@/services'; import { useQuestionList } from '@/services';
import * as Type from '@/common/interface'; import * as Type from '@/common/interface';
@ -44,6 +49,7 @@ const Questions: FC = () => {
/> />
</Col> </Col>
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0"> <Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
<CustomSidebar />
{!loggedUser.username && ( {!loggedUser.username && (
<div className="card mb-4"> <div className="card mb-4">
<div className="card-body"> <div className="card-body">

View File

@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { usePageTags } from '@/hooks'; import { usePageTags } from '@/hooks';
import * as Type from '@/common/interface'; import * as Type from '@/common/interface';
import { FollowingTags } from '@/components'; import { FollowingTags, CustomSidebar } from '@/components';
import { import {
useTagInfo, useTagInfo,
useFollow, useFollow,
@ -152,6 +152,7 @@ const Questions: FC = () => {
<QuestionList source="tag" data={listData} isLoading={listLoading} /> <QuestionList source="tag" data={listData} isLoading={listLoading} />
</Col> </Col>
<Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0"> <Col xxl={3} lg={4} sm={12} className="mt-5 mt-lg-0">
<CustomSidebar />
<FollowingTags /> <FollowingTags />
<HotQuestions /> <HotQuestions />
</Col> </Col>

View File

@ -0,0 +1,55 @@
import React, { FC } from 'react';
import { Card, Col, Carousel } from 'react-bootstrap';
const data = [
{
id: 1,
url: require('@/assets/images/carousel-wecom-1.jpg'),
},
{
id: 2,
url: require('@/assets/images/carousel-wecom-2.jpg'),
},
{
id: 3,
url: require('@/assets/images/carousel-wecom-3.jpg'),
},
{
id: 4,
url: require('@/assets/images/carousel-wecom-4.jpg'),
},
{
id: 5,
url: require('@/assets/images/carousel-wecom-5.jpg'),
},
];
const Index: FC = () => {
return (
<Col lg={4} className="mx-auto mt-3 py-5">
<Card>
<Card.Body>
<h3 className="text-center pt-3 mb-3">WeCom Login</h3>
<p className="text-danger text-center">
Login failed, please allow this app to access your email information
before try again.
</p>
<Carousel controls={false}>
{data.map((item) => (
<Carousel.Item key={item.id}>
<img
className="d-block w-100"
src={item.url}
alt="First slide"
/>
</Carousel.Item>
))}
</Carousel>
</Card.Body>
</Card>
</Col>
);
};
export default Index;

View File

@ -0,0 +1,19 @@
import { memo } from 'react';
import { Container } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom';
import { userCenterStore } from '@/stores';
import WeCom from './components/WeCom';
const Index = () => {
const [searchParam] = useSearchParams();
const { agent: ucAgent } = userCenterStore();
let agentName = ucAgent?.agent_info.name || '';
if (searchParam.get('agent_name')) {
agentName = searchParam.get('agent_name') || '';
}
return <Container>{/^WeCom/i.test(agentName) ? <WeCom /> : null}</Container>;
};
export default memo(Index);

View File

@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
import QrCode from 'qrcode'; import QrCode from 'qrcode';
import { userCenterStore } from '@/stores'; import { userCenterStore } from '@/stores';
import { guard } from '@/utils'; import { guard, getUserAgentType } from '@/utils';
import { getLoginConf, checkLoginResult } from './wecom.service'; import { getLoginConf, checkLoginResult } from './wecom.service';
@ -47,7 +47,11 @@ const Index: FC = () => {
return; return;
} }
getLoginConf().then((res) => { getLoginConf().then((res) => {
handleQrCode(res?.redirect_url); if (getUserAgentType() === 'wxwork') {
window.location.replace(res?.redirect_url);
} else {
handleQrCode(res?.redirect_url);
}
handleLoginResult(res?.key); handleLoginResult(res?.key);
}); });
}, [agentName]); }, [agentName]);
@ -56,7 +60,7 @@ const Index: FC = () => {
clearTimeout(checkTimer); clearTimeout(checkTimer);
}; };
}, []); }, []);
if (/WeCom/i.test(agentName)) { if (/WeCom/i.test(agentName) && getUserAgentType() !== 'wxwork') {
return ( return (
<Card className="text-center"> <Card className="text-center">
<Card.Body> <Card.Body>

View File

@ -1,4 +1,4 @@
import React, { memo, FC } from 'react'; import { memo, FC } from 'react';
import { Button } from 'react-bootstrap'; import { Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';

View File

@ -362,6 +362,10 @@ const routes: RouteNode[] = [
}, },
], ],
}, },
{
path: '/user-center/auth-failed',
page: 'pages/UserCenter/AuthFailed',
},
// for review // for review
{ {
path: 'review', path: 'review',
@ -375,10 +379,6 @@ const routes: RouteNode[] = [
path: '50x', path: '50x',
page: 'pages/50X', page: 'pages/50X',
}, },
{
path: 'login-fail',
page: 'pages/LoginFail',
},
], ],
}, },
{ {

View File

@ -5,11 +5,13 @@ interface IType {
custom_head: string; custom_head: string;
custom_header: string; custom_header: string;
custom_footer: string; custom_footer: string;
custom_sidebar: string;
update: (params: { update: (params: {
custom_css?: string; custom_css?: string;
custom_head?: string; custom_head?: string;
custom_header?: string; custom_header?: string;
custom_footer?: string; custom_footer?: string;
custom_sidebar?: string;
}) => void; }) => void;
} }
@ -18,6 +20,7 @@ const loginSetting = create<IType>((set) => ({
custom_head: '', custom_head: '',
custom_header: '', custom_header: '',
custom_footer: '', custom_footer: '',
custom_sidebar: '',
update: (params) => update: (params) =>
set((state) => { set((state) => {
return { return {

View File

@ -1,5 +1,7 @@
import i18next from 'i18next'; import i18next from 'i18next';
import pattern from '@/common/pattern';
const Diff = require('diff'); const Diff = require('diff');
function thousandthDivision(num) { function thousandthDivision(num) {
@ -254,6 +256,23 @@ function base64ToSvg(base64: string) {
return str; return str;
} }
// Determine whether the user is in WeChat or Enterprise WeChat or DingTalk, and return the corresponding type
function getUserAgentType() {
const ua = navigator.userAgent.toLowerCase();
if (pattern.wxwork.test(ua)) {
return 'wxwork';
}
// if (pattern.wx.test(ua)) {
// return 'weixin';
// }
// if (pattern.dingtalk.test(ua)) {
// return 'dingtalk';
// }
return null;
}
export { export {
thousandthDivision, thousandthDivision,
formatCount, formatCount,
@ -270,4 +289,5 @@ export {
handleFormError, handleFormError,
diffText, diffText,
base64ToSvg, base64ToSvg,
getUserAgentType,
}; };