mirror of https://gitee.com/answerdev/answer.git
Merge branch 'ui' into 'main'
Ui See merge request opensource/answer!19
This commit is contained in:
commit
8287915659
|
@ -26,6 +26,7 @@ module.exports = {
|
||||||
const config = configFunction(proxy, allowedHost);
|
const config = configFunction(proxy, allowedHost);
|
||||||
config.proxy = {
|
config.proxy = {
|
||||||
'/answer': {
|
'/answer': {
|
||||||
|
// target: "http://10.0.20.84:8080",
|
||||||
target: 'http://10.0.10.98:2060',
|
target: 'http://10.0.10.98:2060',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
secure: false,
|
secure: false,
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.5.0",
|
"react-bootstrap": "^2.5.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-helmet": "^6.1.0",
|
"react-helmet-async": "^1.3.0",
|
||||||
"react-i18next": "^11.18.3",
|
"react-i18next": "^11.18.3",
|
||||||
"react-router-dom": "^6.4.0",
|
"react-router-dom": "^6.4.0",
|
||||||
"swr": "^1.3.0",
|
"swr": "^1.3.0",
|
||||||
|
|
|
@ -67,7 +67,7 @@ specifiers:
|
||||||
react-app-rewired: ^2.2.1
|
react-app-rewired: ^2.2.1
|
||||||
react-bootstrap: ^2.5.0
|
react-bootstrap: ^2.5.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
react-helmet: ^6.1.0
|
react-helmet-async: ^1.3.0
|
||||||
react-i18next: ^11.18.3
|
react-i18next: ^11.18.3
|
||||||
react-router-dom: ^6.4.0
|
react-router-dom: ^6.4.0
|
||||||
react-scripts: 5.0.1
|
react-scripts: 5.0.1
|
||||||
|
@ -102,7 +102,7 @@ dependencies:
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-bootstrap: 2.5.0_7ey2zzynotv32rpkwno45fsx4e
|
react-bootstrap: 2.5.0_7ey2zzynotv32rpkwno45fsx4e
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
react-helmet: 6.1.0_react@18.2.0
|
react-helmet-async: 1.3.0_biqbaboplfbrettd7655fr4n2y
|
||||||
react-i18next: 11.18.6_ulhmqqxshznzmtuvahdi5nasbq
|
react-i18next: 11.18.6_ulhmqqxshznzmtuvahdi5nasbq
|
||||||
react-router-dom: 6.4.0_biqbaboplfbrettd7655fr4n2y
|
react-router-dom: 6.4.0_biqbaboplfbrettd7655fr4n2y
|
||||||
swr: 1.3.0_react@18.2.0
|
swr: 1.3.0_react@18.2.0
|
||||||
|
@ -9581,16 +9581,19 @@ packages:
|
||||||
resolution: {integrity: sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==}
|
resolution: {integrity: sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-helmet/6.1.0_react@18.2.0:
|
/react-helmet-async/1.3.0_biqbaboplfbrettd7655fr4n2y:
|
||||||
resolution: {integrity: sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==}
|
resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=16.3.0'
|
react: ^16.6.0 || ^17.0.0 || ^18.0.0
|
||||||
|
react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
object-assign: 4.1.1
|
'@babel/runtime': 7.19.0
|
||||||
|
invariant: 2.2.4
|
||||||
prop-types: 15.8.1
|
prop-types: 15.8.1
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0_react@18.2.0
|
||||||
react-fast-compare: 3.2.0
|
react-fast-compare: 3.2.0
|
||||||
react-side-effect: 2.1.2_react@18.2.0
|
shallowequal: 1.1.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-i18next/11.18.6_ulhmqqxshznzmtuvahdi5nasbq:
|
/react-i18next/11.18.6_ulhmqqxshznzmtuvahdi5nasbq:
|
||||||
|
@ -9747,14 +9750,6 @@ packages:
|
||||||
- webpack-hot-middleware
|
- webpack-hot-middleware
|
||||||
- webpack-plugin-serve
|
- webpack-plugin-serve
|
||||||
|
|
||||||
/react-side-effect/2.1.2_react@18.2.0:
|
|
||||||
resolution: {integrity: sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16.3.0 || ^17.0.0 || ^18.0.0
|
|
||||||
dependencies:
|
|
||||||
react: 18.2.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-transition-group/4.4.5_biqbaboplfbrettd7655fr4n2y:
|
/react-transition-group/4.4.5_biqbaboplfbrettd7655fr4n2y:
|
||||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -10257,6 +10252,10 @@ packages:
|
||||||
/setprototypeof/1.2.0:
|
/setprototypeof/1.2.0:
|
||||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||||
|
|
||||||
|
/shallowequal/1.1.0:
|
||||||
|
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/shebang-command/2.0.0:
|
/shebang-command/2.0.0:
|
||||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
|
@ -80,7 +80,7 @@ export interface RegisterReqParams extends LoginReqParams {
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ModifyPassReq {
|
export interface ModifyPasswordReq {
|
||||||
old_pass: string;
|
old_pass: string;
|
||||||
pass: string;
|
pass: string;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ export interface ImgCodeRes {
|
||||||
verify: boolean;
|
verify: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PssRetReq extends ImgCodeReq {
|
export interface PasswordResetReq extends ImgCodeReq {
|
||||||
e_mail: string;
|
e_mail: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,11 +145,11 @@ export interface CheckImgReq {
|
||||||
action: 'login' | 'e_mail' | 'find_pass';
|
action: 'login' | 'e_mail' | 'find_pass';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NoticeSetReq {
|
export interface SetNoticeReq {
|
||||||
notice_switch: boolean;
|
notice_switch: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface QuDetailRes {
|
export interface QuestionDetailRes {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
|
|
|
@ -5,23 +5,35 @@ import { Avatar } from '@answer/components';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: any;
|
data: any;
|
||||||
|
showAvatar?: boolean;
|
||||||
avatarSize?: string;
|
avatarSize?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Index: FC<Props> = ({
|
const Index: FC<Props> = ({
|
||||||
data,
|
data,
|
||||||
|
showAvatar = true,
|
||||||
avatarSize = '20px',
|
avatarSize = '20px',
|
||||||
className = 'fs-14',
|
className = 'fs-14',
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={`text-secondary ${className}`}>
|
<div className={`text-secondary ${className}`}>
|
||||||
|
{data.status !== 'deleted' ? (
|
||||||
<Link to={`/users/${data?.username}`}>
|
<Link to={`/users/${data?.username}`}>
|
||||||
|
{showAvatar && (
|
||||||
<Avatar avatar={data?.avatar} size={avatarSize} className="me-1" />
|
<Avatar avatar={data?.avatar} size={avatarSize} className="me-1" />
|
||||||
|
)}
|
||||||
|
<span className="me-1 text-break">{data?.display_name}</span>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={`/users/${data?.username}`} className="me-1 text-break">
|
) : (
|
||||||
{data?.display_name}
|
<>
|
||||||
</Link>
|
{showAvatar && (
|
||||||
|
<Avatar avatar={data?.avatar} size={avatarSize} className="me-1" />
|
||||||
|
)}
|
||||||
|
<span className="me-1 text-break">{data?.display_name}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<span className="fw-bold">{data?.rank}</span>
|
<span className="fw-bold">{data?.rank}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,15 +17,20 @@ const ActionBar = ({
|
||||||
onReply,
|
onReply,
|
||||||
onVote,
|
onVote,
|
||||||
onAction,
|
onAction,
|
||||||
|
userStatus = '',
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
|
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-flex justify-content-between fs-14">
|
<div className="d-flex justify-content-between fs-14">
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center text-secondary">
|
||||||
|
{userStatus !== 'deleted' ? (
|
||||||
<Link to={`/users/${username}`}>{nickName}</Link>
|
<Link to={`/users/${username}`}>{nickName}</Link>
|
||||||
<span className="mx-1 text-secondary">•</span>
|
) : (
|
||||||
<FormatTime time={createdAt} className="text-secondary me-3" />
|
<span>{nickName}</span>
|
||||||
|
)}
|
||||||
|
<span className="mx-1">•</span>
|
||||||
|
<FormatTime time={createdAt} className="me-3" />
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|
|
@ -36,7 +36,7 @@ const Form = ({
|
||||||
<Mentions pageUsers={pageUsers.getUsers()}>
|
<Mentions pageUsers={pageUsers.getUsers()}>
|
||||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||||
</Mentions>
|
</Mentions>
|
||||||
<div className="text-muted fs-14">{t(`tip_${mode}`)}</div>
|
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||||
</div>
|
</div>
|
||||||
{type === 'edit' ? (
|
{type === 'edit' ? (
|
||||||
<div className="d-flex flex-column">
|
<div className="d-flex flex-column">
|
||||||
|
|
|
@ -22,7 +22,7 @@ const Form = ({ userName, onSendReply, onCancel, mode }) => {
|
||||||
<Mentions pageUsers={pageUsers.getUsers()}>
|
<Mentions pageUsers={pageUsers.getUsers()}>
|
||||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||||
</Mentions>
|
</Mentions>
|
||||||
<div className="text-muted fs-14">{t(`tip_${mode}`)}</div>
|
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex flex-column">
|
<div className="d-flex flex-column">
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -269,6 +269,7 @@ const Comment = ({ objectId, mode }) => {
|
||||||
voteCount={item.vote_count}
|
voteCount={item.vote_count}
|
||||||
isVote={item.is_vote}
|
isVote={item.is_vote}
|
||||||
memberActions={item.member_actions}
|
memberActions={item.member_actions}
|
||||||
|
userStatus={item.user_status}
|
||||||
onReply={() => {
|
onReply={() => {
|
||||||
handleReply(item.comment_id);
|
handleReply(item.comment_id);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { useSearchParams, NavLink, Link, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { Avatar, Icon } from '@answer/components';
|
import { Avatar, Icon } from '@answer/components';
|
||||||
import { userInfoStore, siteInfoStore, interfaceStore } from '@answer/stores';
|
import { userInfoStore, siteInfoStore, interfaceStore } from '@answer/stores';
|
||||||
import { logout, useQueryNotificationRedDot } from '@answer/api';
|
import { logout, useQueryNotificationStatus } from '@answer/api';
|
||||||
import Storage from '@answer/utils/storage';
|
import Storage from '@answer/utils/storage';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
@ -28,7 +28,7 @@ const Header: FC = () => {
|
||||||
const [searchStr, setSearch] = useState('');
|
const [searchStr, setSearch] = useState('');
|
||||||
const siteInfo = siteInfoStore((state) => state.siteInfo);
|
const siteInfo = siteInfoStore((state) => state.siteInfo);
|
||||||
const { interface: interfaceInfo } = interfaceStore();
|
const { interface: interfaceInfo } = interfaceStore();
|
||||||
const { data: redDot } = useQueryNotificationRedDot();
|
const { data: redDot } = useQueryNotificationStatus();
|
||||||
const handleInput = (val) => {
|
const handleInput = (val) => {
|
||||||
setSearch(val);
|
setSearch(val);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Modal } from '@answer/components';
|
import { Modal } from '@answer/components';
|
||||||
import { useReportModal, useToast } from '@answer/hooks';
|
import { useReportModal, useToast } from '@answer/hooks';
|
||||||
import { questionDelete, answerDelete } from '@answer/api';
|
import { deleteQuestion, deleteAnswer } from '@answer/api';
|
||||||
import { isLogin } from '@answer/utils';
|
import { isLogin } from '@answer/utils';
|
||||||
import Share from '../Share';
|
import Share from '../Share';
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ const Index: FC<IProps> = ({
|
||||||
confirmBtnVariant: 'danger',
|
confirmBtnVariant: 'danger',
|
||||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
questionDelete({
|
deleteQuestion({
|
||||||
id: qid,
|
id: qid,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
toast.onShow({
|
toast.onShow({
|
||||||
|
@ -82,7 +82,7 @@ const Index: FC<IProps> = ({
|
||||||
confirmBtnVariant: 'danger',
|
confirmBtnVariant: 'danger',
|
||||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
answerDelete({
|
deleteAnswer({
|
||||||
id: aid,
|
id: aid,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
// refersh page
|
// refersh page
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FC, memo } from 'react';
|
import { FC } from 'react';
|
||||||
import { Pagination } from 'react-bootstrap';
|
import { Pagination } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||||
|
@ -47,6 +47,7 @@ const PageItem = ({ page, currentPage, path }: PageItemProps) => {
|
||||||
href={path}
|
href={path}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
navigate(path);
|
navigate(path);
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}}>
|
}}>
|
||||||
|
@ -111,7 +112,7 @@ const Index: FC<Props> = ({
|
||||||
)}
|
)}
|
||||||
{currentPage === 4 && totalPage > 6 && (
|
{currentPage === 4 && totalPage > 6 && (
|
||||||
<PageItem
|
<PageItem
|
||||||
key="6"
|
key="page6"
|
||||||
page={6}
|
page={6}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
path={handleParams(6)}
|
path={handleParams(6)}
|
||||||
|
@ -133,13 +134,13 @@ const Index: FC<Props> = ({
|
||||||
{currentPage >= 5 && (
|
{currentPage >= 5 && (
|
||||||
<>
|
<>
|
||||||
<PageItem
|
<PageItem
|
||||||
key="prev2"
|
key={realPage - 2}
|
||||||
page={realPage - 2}
|
page={realPage - 2}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
path={handleParams(realPage - 2)}
|
path={handleParams(realPage - 2)}
|
||||||
/>
|
/>
|
||||||
<PageItem
|
<PageItem
|
||||||
key="prev1"
|
key={realPage - 1}
|
||||||
page={realPage - 1}
|
page={realPage - 1}
|
||||||
currentPage={currentPage}
|
currentPage={currentPage}
|
||||||
path={handleParams(realPage - 1)}
|
path={handleParams(realPage - 1)}
|
||||||
|
@ -194,4 +195,4 @@ const Index: FC<Props> = ({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default memo(Index);
|
export default Index;
|
||||||
|
|
|
@ -5,7 +5,14 @@ import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { useQuestionList } from '@answer/api';
|
import { useQuestionList } from '@answer/api';
|
||||||
import type * as Type from '@answer/common/interface';
|
import type * as Type from '@answer/common/interface';
|
||||||
import { Icon, Tag, Pagination, FormatTime, Empty } from '@answer/components';
|
import {
|
||||||
|
Icon,
|
||||||
|
Tag,
|
||||||
|
Pagination,
|
||||||
|
FormatTime,
|
||||||
|
Empty,
|
||||||
|
BaseUserCard,
|
||||||
|
} from '@answer/components';
|
||||||
|
|
||||||
const QuestionOrderKeys: Type.QuestionOrderBy[] = [
|
const QuestionOrderKeys: Type.QuestionOrderBy[] = [
|
||||||
'newest',
|
'newest',
|
||||||
|
@ -25,12 +32,11 @@ const QuestionLastUpdate = ({ q }) => {
|
||||||
// question answered
|
// question answered
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<a
|
<BaseUserCard
|
||||||
className="p-0"
|
data={q.last_answered_user_info}
|
||||||
href={`/users/${q.last_answered_user_info.username}`}>
|
showAvatar={false}
|
||||||
{q.last_answered_user_info.display_name}
|
className="me-1"
|
||||||
</a>
|
/>
|
||||||
<span className="fw-bold px-1">{q.last_answered_user_info.rank}</span>
|
|
||||||
•
|
•
|
||||||
<FormatTime
|
<FormatTime
|
||||||
time={q.update_time}
|
time={q.update_time}
|
||||||
|
@ -45,10 +51,11 @@ const QuestionLastUpdate = ({ q }) => {
|
||||||
// question modified
|
// question modified
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<a className="p-0" href={`/users/${q.update_user_info.username}`}>
|
<BaseUserCard
|
||||||
{q.update_user_info.display_name}
|
data={q.update_user_info}
|
||||||
</a>
|
showAvatar={false}
|
||||||
<span className="fw-bold px-1">{q.update_user_info.rank}</span>
|
className="me-1"
|
||||||
|
/>
|
||||||
•
|
•
|
||||||
<FormatTime
|
<FormatTime
|
||||||
time={q.edit_time}
|
time={q.edit_time}
|
||||||
|
@ -62,10 +69,7 @@ const QuestionLastUpdate = ({ q }) => {
|
||||||
// default: asked
|
// default: asked
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<a className="p-0" href={`/users/${q.user_info.username}`}>
|
<BaseUserCard data={q.user_info} showAvatar={false} className="me-1" />
|
||||||
{q.user_info.display_name}
|
|
||||||
</a>
|
|
||||||
<strong className="px-1">{q.user_info.rank}</strong>
|
|
||||||
•
|
•
|
||||||
<FormatTime
|
<FormatTime
|
||||||
time={q.create_time}
|
time={q.create_time}
|
||||||
|
|
|
@ -50,7 +50,7 @@ const Index: FC<IProps> = ({ type, qid, aid, title }) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window.navigator?.canShare?.({ text: '111' })) {
|
if (window.navigator?.canShare?.({ text: 'can_share' })) {
|
||||||
setSystemShareState(true);
|
setSystemShareState(true);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||||
import { Button, Col } from 'react-bootstrap';
|
import { Button, Col } from 'react-bootstrap';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
import { Trans, useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { emailReSend, checkImgCode } from '@answer/api';
|
import { resendEmail, checkImgCode } from '@answer/api';
|
||||||
import { PicAuthCodeModal } from '@answer/components/Modal';
|
import { PicAuthCodeModal } from '@answer/components/Modal';
|
||||||
import type {
|
import type {
|
||||||
ImgCodeRes,
|
ImgCodeRes,
|
||||||
|
@ -53,7 +53,7 @@ const Index: React.FC<IProps> = ({ visible = false }) => {
|
||||||
captcha_id: imgCode.captcha_id,
|
captcha_id: imgCode.captcha_id,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
emailReSend(obj)
|
resendEmail(obj)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
setSuccess(true);
|
setSuccess(true);
|
||||||
setModalState(false);
|
setModalState(false);
|
||||||
|
|
|
@ -12,14 +12,22 @@ interface Props {
|
||||||
const Index: FC<Props> = ({ data, time, preFix }) => {
|
const Index: FC<Props> = ({ data, time, preFix }) => {
|
||||||
return (
|
return (
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
|
{data?.status !== 'deleted' ? (
|
||||||
<Link to={`/users/${data?.username}`}>
|
<Link to={`/users/${data?.username}`}>
|
||||||
<Avatar avatar={data?.avatar} size="40px" className="me-2" />
|
<Avatar avatar={data?.avatar} size="40px" className="me-2" />
|
||||||
</Link>
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Avatar avatar={data?.avatar} size="40px" className="me-2" />
|
||||||
|
)}
|
||||||
<div className="fs-14 text-secondary">
|
<div className="fs-14 text-secondary">
|
||||||
<div>
|
<div>
|
||||||
|
{data?.status !== 'deleted' ? (
|
||||||
<Link to={`/users/${data?.username}`} className="me-1 text-break">
|
<Link to={`/users/${data?.username}`} className="me-1 text-break">
|
||||||
{data?.display_name}
|
{data?.display_name}
|
||||||
</Link>
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span className="me-1 text-break">{data?.display_name}</span>
|
||||||
|
)}
|
||||||
<span className="fw-bold">{data?.rank}</span>
|
<span className="fw-bold">{data?.rank}</span>
|
||||||
</div>
|
</div>
|
||||||
{time && <FormatTime time={time} preFix={preFix} />}
|
{time && <FormatTime time={time} preFix={preFix} />}
|
||||||
|
|
|
@ -848,7 +848,7 @@
|
||||||
"interface": {
|
"interface": {
|
||||||
"page_title": "Interface",
|
"page_title": "Interface",
|
||||||
"logo": {
|
"logo": {
|
||||||
"label": "Logo",
|
"label": "Logo (optional)",
|
||||||
"msg": "Site logo cannot be empty.",
|
"msg": "Site logo cannot be empty.",
|
||||||
"text": "You can upload your image or <1>reset</1> it to the site title text."
|
"text": "You can upload your image or <1>reset</1> it to the site title text."
|
||||||
},
|
},
|
||||||
|
|
|
@ -30,7 +30,7 @@ const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted'];
|
||||||
const Answers: FC = () => {
|
const Answers: FC = () => {
|
||||||
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
||||||
const curFilter = urlSearchParams.get('status') || answerFilterItems[0];
|
const curFilter = urlSearchParams.get('status') || answerFilterItems[0];
|
||||||
const pageSize = 20;
|
const PAGE_SIZE = 20;
|
||||||
const curPage = Number(urlSearchParams.get('page')) || 1;
|
const curPage = Number(urlSearchParams.get('page')) || 1;
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'admin.answers' });
|
const { t } = useTranslation('translation', { keyPrefix: 'admin.answers' });
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ const Answers: FC = () => {
|
||||||
isLoading,
|
isLoading,
|
||||||
mutate: refreshList,
|
mutate: refreshList,
|
||||||
} = useAnswerSearch({
|
} = useAnswerSearch({
|
||||||
page_size: pageSize,
|
page_size: PAGE_SIZE,
|
||||||
page: curPage,
|
page: curPage,
|
||||||
status: curFilter as Type.AdminContentsFilterBy,
|
status: curFilter as Type.AdminContentsFilterBy,
|
||||||
});
|
});
|
||||||
|
@ -189,7 +189,7 @@ const Answers: FC = () => {
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={curPage}
|
currentPage={curPage}
|
||||||
totalSize={count}
|
totalSize={count}
|
||||||
pageSize={pageSize}
|
pageSize={PAGE_SIZE}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -23,14 +23,14 @@ const Flags: FC = () => {
|
||||||
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
||||||
const curFilter = urlSearchParams.get('status') || flagFilterKeys[0];
|
const curFilter = urlSearchParams.get('status') || flagFilterKeys[0];
|
||||||
const curType = urlSearchParams.get('type') || flagTypeKeys[0];
|
const curType = urlSearchParams.get('type') || flagTypeKeys[0];
|
||||||
const pageSize = 20;
|
const PAGE_SIZE = 20;
|
||||||
const curPage = Number(urlSearchParams.get('page')) || 1;
|
const curPage = Number(urlSearchParams.get('page')) || 1;
|
||||||
const {
|
const {
|
||||||
data: listData,
|
data: listData,
|
||||||
isLoading,
|
isLoading,
|
||||||
mutate: refreshList,
|
mutate: refreshList,
|
||||||
} = useFlagSearch({
|
} = useFlagSearch({
|
||||||
page_size: pageSize,
|
page_size: PAGE_SIZE,
|
||||||
page: curPage,
|
page: curPage,
|
||||||
status: curFilter as Type.FlagStatus,
|
status: curFilter as Type.FlagStatus,
|
||||||
object_type: curType as Type.FlagType,
|
object_type: curType as Type.FlagType,
|
||||||
|
@ -159,7 +159,7 @@ const Flags: FC = () => {
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={curPage}
|
currentPage={curPage}
|
||||||
totalSize={count}
|
totalSize={count}
|
||||||
pageSize={pageSize}
|
pageSize={PAGE_SIZE}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { useEditStatusModal, useReportModal } from '@answer/hooks';
|
||||||
import {
|
import {
|
||||||
useQuestionSearch,
|
useQuestionSearch,
|
||||||
changeQuestionStatus,
|
changeQuestionStatus,
|
||||||
questionDelete,
|
deleteQuestion,
|
||||||
} from '@answer/api';
|
} from '@answer/api';
|
||||||
import * as Type from '@answer/common/interface';
|
import * as Type from '@answer/common/interface';
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ const questionFilterItems: Type.AdminContentsFilterBy[] = [
|
||||||
'deleted',
|
'deleted',
|
||||||
];
|
];
|
||||||
|
|
||||||
const pageSize = 20;
|
const PAGE_SIZE = 20;
|
||||||
const Questions: FC = () => {
|
const Questions: FC = () => {
|
||||||
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
||||||
const curFilter = urlSearchParams.get('status') || questionFilterItems[0];
|
const curFilter = urlSearchParams.get('status') || questionFilterItems[0];
|
||||||
|
@ -47,7 +47,7 @@ const Questions: FC = () => {
|
||||||
isLoading,
|
isLoading,
|
||||||
mutate: refreshList,
|
mutate: refreshList,
|
||||||
} = useQuestionSearch({
|
} = useQuestionSearch({
|
||||||
page_size: pageSize,
|
page_size: PAGE_SIZE,
|
||||||
page: curPage,
|
page: curPage,
|
||||||
status: curFilter as Type.AdminContentsFilterBy,
|
status: curFilter as Type.AdminContentsFilterBy,
|
||||||
});
|
});
|
||||||
|
@ -89,7 +89,7 @@ const Questions: FC = () => {
|
||||||
confirmBtnVariant: 'danger',
|
confirmBtnVariant: 'danger',
|
||||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||||
onConfirm: () => {
|
onConfirm: () => {
|
||||||
questionDelete({
|
deleteQuestion({
|
||||||
id,
|
id,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
refreshList();
|
refreshList();
|
||||||
|
@ -207,7 +207,7 @@ const Questions: FC = () => {
|
||||||
<Pagination
|
<Pagination
|
||||||
currentPage={curPage}
|
currentPage={curPage}
|
||||||
totalSize={count}
|
totalSize={count}
|
||||||
pageSize={pageSize}
|
pageSize={PAGE_SIZE}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -1,140 +0,0 @@
|
||||||
import { FC } from 'react';
|
|
||||||
import {
|
|
||||||
Container,
|
|
||||||
Row,
|
|
||||||
Col,
|
|
||||||
Button,
|
|
||||||
Table,
|
|
||||||
Figure,
|
|
||||||
Stack,
|
|
||||||
} from 'react-bootstrap';
|
|
||||||
|
|
||||||
import { AccordionNav } from '@answer/components';
|
|
||||||
import { ADMIN_NAV_MENUS } from '@answer/common/constants';
|
|
||||||
|
|
||||||
import '../index.scss';
|
|
||||||
|
|
||||||
const UserOverview: FC = () => {
|
|
||||||
return (
|
|
||||||
<Container className="admin-container">
|
|
||||||
<Row>
|
|
||||||
<Col lg={2}>
|
|
||||||
<AccordionNav menus={ADMIN_NAV_MENUS} />
|
|
||||||
</Col>
|
|
||||||
<Col lg={10}>
|
|
||||||
<Button variant="outline-secondary" size="sm">
|
|
||||||
← Back
|
|
||||||
</Button>
|
|
||||||
<h5 className="mb-3 mt-4">Profile</h5>
|
|
||||||
<Table className="mb-5">
|
|
||||||
<tbody className="align-middle">
|
|
||||||
<tr>
|
|
||||||
<td>ID</td>
|
|
||||||
<td>1030000000091295</td>
|
|
||||||
<td />
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Display name</td>
|
|
||||||
<td>Jim Green</td>
|
|
||||||
<td />
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>username</td>
|
|
||||||
<td>jimgreen</td>
|
|
||||||
<td>
|
|
||||||
<Button variant="link" size="sm">
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Profile image</td>
|
|
||||||
<td>
|
|
||||||
<Figure.Image
|
|
||||||
width={48}
|
|
||||||
height={48}
|
|
||||||
className="rounded-1 m-0"
|
|
||||||
src="https://gw.alicdn.com/bao/uploaded/i4/1607723262/O1CN01JJCGVD1Zy2jryOhDc_!!1607723262.jpg"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td />
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
<h5 className="mb-3 mt-4">Permissions</h5>
|
|
||||||
<Table className="mb-5">
|
|
||||||
<tbody className="align-middle">
|
|
||||||
<tr>
|
|
||||||
<td>Activated</td>
|
|
||||||
<td>No</td>
|
|
||||||
<td>
|
|
||||||
<Button size="sm" variant="link">
|
|
||||||
Activate
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Admin?</td>
|
|
||||||
<td>No</td>
|
|
||||||
<td>
|
|
||||||
<Button size="sm" variant="link">
|
|
||||||
Grant
|
|
||||||
</Button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Suspended?</td>
|
|
||||||
<td>No</td>
|
|
||||||
<td>
|
|
||||||
<Stack direction="horizontal" gap={1}>
|
|
||||||
<Button size="sm" variant="link" className="text-danger">
|
|
||||||
Suspend
|
|
||||||
</Button>
|
|
||||||
<div className="text-secondary text-nowrap">
|
|
||||||
A suspended user can't log in
|
|
||||||
</div>
|
|
||||||
</Stack>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
<h5 className="mb-3 mt-4">Activity</h5>
|
|
||||||
<Table className="mb-5">
|
|
||||||
<tbody className="align-middle">
|
|
||||||
<tr>
|
|
||||||
<td>Reputation</td>
|
|
||||||
<td>1805</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Answers</td>
|
|
||||||
<td>30</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Questions</td>
|
|
||||||
<td>10</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Created</td>
|
|
||||||
<td>Sep 1, 2022 at 16:00</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Registration IP address</td>
|
|
||||||
<td>11.22.33.44</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Seen</td>
|
|
||||||
<td>Sep 6, 2022 at 09:35</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>Last IP address</td>
|
|
||||||
<td>11.22.33.44</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</Table>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UserOverview;
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { FC, useEffect } from 'react';
|
import { FC, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||||
|
|
||||||
import { SWRConfig } from 'swr';
|
import { SWRConfig } from 'swr';
|
||||||
|
|
||||||
|
@ -56,11 +56,9 @@ const Layout: FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<HelmetProvider>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
{siteInfo ? (
|
{siteInfo && <meta name="description" content={siteInfo.description} />}
|
||||||
<meta name="description" content={siteInfo.description} />
|
|
||||||
) : null}
|
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<SWRConfig
|
<SWRConfig
|
||||||
value={{
|
value={{
|
||||||
|
@ -74,7 +72,7 @@ const Layout: FC = () => {
|
||||||
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
||||||
<Footer />
|
<Footer />
|
||||||
</SWRConfig>
|
</SWRConfig>
|
||||||
</>
|
</HelmetProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,49 +20,26 @@ import type * as Type from '@answer/common/interface';
|
||||||
import SearchQuestion from './components/SearchQuestion';
|
import SearchQuestion from './components/SearchQuestion';
|
||||||
|
|
||||||
interface FormDataItem {
|
interface FormDataItem {
|
||||||
title: {
|
title: Type.FormValue<string>;
|
||||||
value: string;
|
tags: Type.FormValue<Type.Tag[]>;
|
||||||
isInvalid: boolean;
|
content: Type.FormValue<string>;
|
||||||
errorMsg: string;
|
answer: Type.FormValue<string>;
|
||||||
focus?: boolean;
|
|
||||||
};
|
|
||||||
tags: {
|
|
||||||
value: Type.Tag[];
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
focus?: boolean;
|
|
||||||
};
|
|
||||||
content: {
|
|
||||||
value: string;
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
focus?: boolean;
|
|
||||||
};
|
|
||||||
answer: {
|
|
||||||
value: string;
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
focus?: boolean;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
const initFormData = {
|
const initFormData = {
|
||||||
title: {
|
title: {
|
||||||
value: '',
|
value: '',
|
||||||
isInvalid: false,
|
isInvalid: false,
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
focus: false,
|
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
value: [],
|
value: [],
|
||||||
isInvalid: false,
|
isInvalid: false,
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
focus: false,
|
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
value: '',
|
value: '',
|
||||||
isInvalid: false,
|
isInvalid: false,
|
||||||
errorMsg: '',
|
errorMsg: '',
|
||||||
focus: false,
|
|
||||||
},
|
},
|
||||||
answer: {
|
answer: {
|
||||||
value: '',
|
value: '',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { memo, FC } from 'react';
|
import { memo, FC } from 'react';
|
||||||
import { ButtonGroup } from 'react-bootstrap';
|
import { ButtonGroup } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
count: number;
|
count: number;
|
||||||
|
@ -11,6 +11,7 @@ const Index: FC<Props> = ({ count = 0, order = 'default' }) => {
|
||||||
const { t } = useTranslation('translation', {
|
const { t } = useTranslation('translation', {
|
||||||
keyPrefix: 'question_detail.answers',
|
keyPrefix: 'question_detail.answers',
|
||||||
});
|
});
|
||||||
|
const location = useLocation();
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="d-flex align-items-center justify-content-between mt-5 mb-3"
|
className="d-flex align-items-center justify-content-between mt-5 mb-3"
|
||||||
|
@ -20,14 +21,14 @@ const Index: FC<Props> = ({ count = 0, order = 'default' }) => {
|
||||||
</h5>
|
</h5>
|
||||||
<ButtonGroup size="sm">
|
<ButtonGroup size="sm">
|
||||||
<Link
|
<Link
|
||||||
to={`${window.location.pathname}?order=default`}
|
to={`${location.pathname}?order=default`}
|
||||||
className={`btn btn-outline-secondary ${
|
className={`btn btn-outline-secondary ${
|
||||||
order !== 'updated' ? 'active' : ''
|
order !== 'updated' ? 'active' : ''
|
||||||
}`}>
|
}`}>
|
||||||
{t('score')}
|
{t('score')}
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to={`${window.location.pathname}?order=updated`}
|
to={`${location.pathname}?order=updated`}
|
||||||
className={`btn btn-outline-secondary ${
|
className={`btn btn-outline-secondary ${
|
||||||
order === 'updated' ? 'active' : ''
|
order === 'updated' ? 'active' : ''
|
||||||
}`}>
|
}`}>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Container, Row, Col } from 'react-bootstrap';
|
import { Container, Row, Col } from 'react-bootstrap';
|
||||||
import { useParams, useSearchParams } from 'react-router-dom';
|
import { useParams, useSearchParams, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { questionDetail, getAnswers } from '@answer/api';
|
import { questionDetail, getAnswers } from '@answer/api';
|
||||||
import { Pagination, PageTitle } from '@answer/components';
|
import { Pagination, PageTitle } from '@answer/components';
|
||||||
|
@ -9,7 +9,7 @@ import { scrollTop } from '@answer/utils';
|
||||||
import { usePageUsers } from '@answer/hooks';
|
import { usePageUsers } from '@answer/hooks';
|
||||||
import type {
|
import type {
|
||||||
ListResult,
|
ListResult,
|
||||||
QuDetailRes,
|
QuestionDetailRes,
|
||||||
AnswerItem,
|
AnswerItem,
|
||||||
} from '@answer/common/interface';
|
} from '@answer/common/interface';
|
||||||
|
|
||||||
|
@ -25,12 +25,13 @@ import {
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
|
const navigate = useNavigate();
|
||||||
const { qid = '', aid = '' } = useParams();
|
const { qid = '', aid = '' } = useParams();
|
||||||
const [urlSearch] = useSearchParams();
|
const [urlSearch] = useSearchParams();
|
||||||
|
|
||||||
const page = Number(urlSearch.get('page') || 0);
|
const page = Number(urlSearch.get('page') || 0);
|
||||||
const order = urlSearch.get('order') || '';
|
const order = urlSearch.get('order') || '';
|
||||||
const [question, setQuestion] = useState<QuDetailRes | null>(null);
|
const [question, setQuestion] = useState<QuestionDetailRes | null>(null);
|
||||||
const [answers, setAnswers] = useState<ListResult<AnswerItem>>({
|
const [answers, setAnswers] = useState<ListResult<AnswerItem>>({
|
||||||
count: -1,
|
count: -1,
|
||||||
list: [],
|
list: [],
|
||||||
|
@ -75,7 +76,7 @@ const Index = () => {
|
||||||
const initPage = (type: string) => {
|
const initPage = (type: string) => {
|
||||||
if (type === 'delete_question') {
|
if (type === 'delete_question') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.history.back();
|
navigate(-1);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,8 @@ import type * as Type from '@answer/common/interface';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
interface FormDataItem {
|
interface FormDataItem {
|
||||||
answer: {
|
answer: Type.FormValue<string>;
|
||||||
value: string;
|
description: Type.FormValue<string>;
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
};
|
|
||||||
description: {
|
|
||||||
value: string;
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
const initFormData = {
|
const initFormData = {
|
||||||
answer: {
|
answer: {
|
||||||
|
@ -111,7 +103,7 @@ const Ask = () => {
|
||||||
id: aid,
|
id: aid,
|
||||||
};
|
};
|
||||||
modifyAnswer(params).then(() => {
|
modifyAnswer(params).then(() => {
|
||||||
window.location.href = `/questions/${qid}/${aid}`;
|
navigate(`/questions/${qid}/${aid}`);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { FC, memo } from 'react';
|
import { FC, memo } from 'react';
|
||||||
import { ListGroupItem, ButtonGroup, Button } from 'react-bootstrap';
|
import { ListGroupItem, ButtonGroup, Button } from 'react-bootstrap';
|
||||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const sortBtns = [
|
const sortBtns = [
|
||||||
|
@ -21,6 +21,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
const Index: FC<Props> = ({ sort, count = 0 }) => {
|
const Index: FC<Props> = ({ sort, count = 0 }) => {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { t } = useTranslation('translation', {
|
const { t } = useTranslation('translation', {
|
||||||
|
@ -28,7 +29,7 @@ const Index: FC<Props> = ({ sort, count = 0 }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleParams = (order): string => {
|
const handleParams = (order): string => {
|
||||||
const basePath = window.location.pathname;
|
const basePath = location.pathname;
|
||||||
searchParams.delete('page');
|
searchParams.delete('page');
|
||||||
searchParams.set('order', order);
|
searchParams.set('order', order);
|
||||||
const searchStr = searchParams.toString();
|
const searchStr = searchParams.toString();
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { memo, FC } from 'react';
|
import { memo, FC } from 'react';
|
||||||
import { ListGroupItem, Badge } from 'react-bootstrap';
|
import { ListGroupItem, Badge } from 'react-bootstrap';
|
||||||
|
|
||||||
import { Icon, Tag, FormatTime } from '@answer/components';
|
import { Icon, Tag, FormatTime, BaseUserCard } from '@answer/components';
|
||||||
import type { SearchResItem } from '@answer/common/interface';
|
import type { SearchResItem } from '@answer/common/interface';
|
||||||
import { formatCount } from '@answer/utils';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: SearchResItem;
|
data: SearchResItem;
|
||||||
|
@ -28,14 +27,8 @@ const Index: FC<Props> = ({ data }) => {
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div className="d-flex flex-wrap align-items-center fs-14 text-secondary mb-2">
|
<div className="d-flex flex-wrap align-items-center fs-14 text-secondary mb-2">
|
||||||
<a href={`/users/${data.object?.user_info?.username}`}>
|
<BaseUserCard data={data.object?.user_info} showAvatar={false} />
|
||||||
{data.object?.user_info?.display_name}
|
|
||||||
</a>
|
|
||||||
{data.object?.user_info?.rank > 0 && (
|
|
||||||
<span className="fw-bold ms-1">
|
|
||||||
{formatCount(data.object.user_info.rank)}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className="split-dot" />
|
<span className="split-dot" />
|
||||||
<FormatTime
|
<FormatTime
|
||||||
time={data.object?.created_at}
|
time={data.object?.created_at}
|
||||||
|
|
|
@ -9,28 +9,13 @@ import classNames from 'classnames';
|
||||||
import { Editor, EditorRef, PageTitle } from '@answer/components';
|
import { Editor, EditorRef, PageTitle } from '@answer/components';
|
||||||
import { useTagInfo, modifyTag, useQueryRevisions } from '@answer/api';
|
import { useTagInfo, modifyTag, useQueryRevisions } from '@answer/api';
|
||||||
import { userInfoStore } from '@answer/stores';
|
import { userInfoStore } from '@answer/stores';
|
||||||
|
import type * as Type from '@answer/common/interface';
|
||||||
|
|
||||||
interface FormDataItem {
|
interface FormDataItem {
|
||||||
displayName: {
|
displayName: Type.FormValue<string>;
|
||||||
value: string;
|
slugName: Type.FormValue<string>;
|
||||||
isInvalid: boolean;
|
description: Type.FormValue<string>;
|
||||||
errorMsg: string;
|
editSummary: Type.FormValue<string>;
|
||||||
};
|
|
||||||
slugName: {
|
|
||||||
value: string;
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
};
|
|
||||||
description: {
|
|
||||||
value: string;
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
};
|
|
||||||
editSummary: {
|
|
||||||
value: string;
|
|
||||||
isInvalid: boolean;
|
|
||||||
errorMsg: string;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
const initFormData = {
|
const initFormData = {
|
||||||
displayName: {
|
displayName: {
|
||||||
|
|
|
@ -2,10 +2,10 @@ import { FC, memo, useEffect, useState } from 'react';
|
||||||
import { Form, Button } from 'react-bootstrap';
|
import { Form, Button } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { passRetrieve, checkImgCode } from '@answer/api';
|
import { resetPassword, checkImgCode } from '@answer/api';
|
||||||
import type {
|
import type {
|
||||||
ImgCodeRes,
|
ImgCodeRes,
|
||||||
PssRetReq,
|
PasswordResetReq,
|
||||||
FormDataType,
|
FormDataType,
|
||||||
} from '@answer/common/interface';
|
} from '@answer/common/interface';
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ const Index: FC<IProps> = ({ visible = false, callback }) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
const params: PssRetReq = {
|
const params: PasswordResetReq = {
|
||||||
e_mail: formData.e_mail.value,
|
e_mail: formData.e_mail.value,
|
||||||
};
|
};
|
||||||
if (imgCode.verify) {
|
if (imgCode.verify) {
|
||||||
|
@ -78,7 +78,7 @@ const Index: FC<IProps> = ({ visible = false, callback }) => {
|
||||||
params.captcha_id = imgCode.captcha_id;
|
params.captcha_id = imgCode.captcha_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
passRetrieve(params)
|
resetPassword(params)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
callback?.(2, formData.e_mail.value);
|
callback?.(2, formData.e_mail.value);
|
||||||
setModalState(false);
|
setModalState(false);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { FC, memo, useEffect } from 'react';
|
import { FC, memo, useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { accountActivate } from '@answer/api';
|
import { activateAccount } from '@answer/api';
|
||||||
import { userInfoStore } from '@answer/stores';
|
import { userInfoStore } from '@answer/stores';
|
||||||
import { getQueryString } from '@answer/utils';
|
import { getQueryString } from '@answer/utils';
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@ const Index: FC = () => {
|
||||||
const code = getQueryString('code');
|
const code = getQueryString('code');
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
accountActivate(encodeURIComponent(code)).then((res) => {
|
activateAccount(encodeURIComponent(code)).then((res) => {
|
||||||
updateUser(res);
|
updateUser(res);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = '/users/account-activation/success';
|
window.location.replace('/users/account-activation/success');
|
||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,13 +108,9 @@ const Index: React.FC = () => {
|
||||||
setRefresh((pre) => pre + 1);
|
setRefresh((pre) => pre + 1);
|
||||||
}
|
}
|
||||||
if (res.mail_status === 1) {
|
if (res.mail_status === 1) {
|
||||||
const path = Storage.get('ANSWER_PATH');
|
const path = Storage.get('ANSWER_PATH') || '/';
|
||||||
Storage.remove('ANSWER_PATH');
|
Storage.remove('ANSWER_PATH');
|
||||||
if (path) {
|
window.location.replace(path);
|
||||||
window.location.href = path;
|
|
||||||
} else {
|
|
||||||
window.location.href = '/';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setModalState(false);
|
setModalState(false);
|
||||||
|
|
|
@ -37,9 +37,13 @@ const Inbox = ({ data, handleReadNotification }) => {
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className={classNames('py-3', !item.is_read && 'warning')}>
|
className={classNames('py-3', !item.is_read && 'warning')}>
|
||||||
<div>
|
<div>
|
||||||
|
{item.user_info.status !== 'deleted' ? (
|
||||||
<Link to={`/users/${item.user_info.username}`}>
|
<Link to={`/users/${item.user_info.username}`}>
|
||||||
{item.user_info.display_name}
|
{item.user_info.display_name}{' '}
|
||||||
</Link>{' '}
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span>{item.user_info.display_name} </span>
|
||||||
|
)}
|
||||||
{item.notification_action}{' '}
|
{item.notification_action}{' '}
|
||||||
<Link to={url} onClick={() => handleReadNotification(item.id)}>
|
<Link to={url} onClick={() => handleReadNotification(item.id)}>
|
||||||
{item.object_info.title}
|
{item.object_info.title}
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { useParams, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useQueryNotifications,
|
useQueryNotifications,
|
||||||
clearUnReadNotification,
|
clearUnreadNotification,
|
||||||
clearNotificationRedDot,
|
clearNotificationStatus,
|
||||||
readNotification,
|
readNotification,
|
||||||
} from '@answer/api';
|
} from '@answer/api';
|
||||||
import { PageTitle } from '@answer/components';
|
import { PageTitle } from '@answer/components';
|
||||||
|
@ -29,7 +29,7 @@ const Notifications = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearNotificationRedDot(type);
|
clearNotificationStatus(type);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -56,7 +56,7 @@ const Notifications = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleUnreadNotification = async () => {
|
const handleUnreadNotification = async () => {
|
||||||
await clearUnReadNotification(type);
|
await clearUnreadNotification(type);
|
||||||
mutate();
|
mutate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Container, Col, Form, Button } from 'react-bootstrap';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { passRetrieveSet } from '@answer/api';
|
import { replacementPassword } from '@answer/api';
|
||||||
import { userInfoStore } from '@answer/stores';
|
import { userInfoStore } from '@answer/stores';
|
||||||
import { getQueryString, isLogin } from '@answer/utils';
|
import { getQueryString, isLogin } from '@answer/utils';
|
||||||
import type { FormDataType } from '@answer/common/interface';
|
import type { FormDataType } from '@answer/common/interface';
|
||||||
|
@ -98,7 +98,7 @@ const Index: React.FC = () => {
|
||||||
console.error('code is required');
|
console.error('code is required');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
passRetrieveSet({
|
replacementPassword({
|
||||||
code: encodeURIComponent(code),
|
code: encodeURIComponent(code),
|
||||||
pass: formData.pass.value,
|
pass: formData.pass.value,
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { FC, memo } from 'react';
|
||||||
import { ListGroup, ListGroupItem } from 'react-bootstrap';
|
import { ListGroup, ListGroupItem } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { Icon, FormatTime, Tag } from '@answer/components';
|
import { Icon, FormatTime, Tag, BaseUserCard } from '@answer/components';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
@ -31,15 +31,10 @@ const Index: FC<Props> = ({ visible, tabName, data }) => {
|
||||||
</h6>
|
</h6>
|
||||||
<div className="d-flex align-items-center fs-14 text-secondary mb-2">
|
<div className="d-flex align-items-center fs-14 text-secondary mb-2">
|
||||||
{tabName === 'bookmarks' && (
|
{tabName === 'bookmarks' && (
|
||||||
<div className="d-flex">
|
<>
|
||||||
<a
|
<BaseUserCard data={item.user_info} showAvatar={false} />
|
||||||
href={`/users/${item.user_info?.username}`}
|
|
||||||
className="me-1">
|
|
||||||
{item.user_info?.display_name}
|
|
||||||
</a>
|
|
||||||
<strong>{item.user_info?.rank}</strong>
|
|
||||||
<span className="split-dot" />
|
<span className="split-dot" />
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
<FormatTime
|
<FormatTime
|
||||||
time={item.create_time}
|
time={item.create_time}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { FC, memo } from 'react';
|
import { FC, memo } from 'react';
|
||||||
import { ButtonGroup, Button } from 'react-bootstrap';
|
import { ButtonGroup, Button } from 'react-bootstrap';
|
||||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
const sortBtns = [
|
const sortBtns = [
|
||||||
|
@ -26,10 +26,11 @@ const Index: FC<Props> = ({
|
||||||
}) => {
|
}) => {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const location = useLocation();
|
||||||
const { t } = useTranslation('translation', { keyPrefix: 'personal' });
|
const { t } = useTranslation('translation', { keyPrefix: 'personal' });
|
||||||
|
|
||||||
const handleParams = (order): string => {
|
const handleParams = (order): string => {
|
||||||
const basePath = window.location.pathname;
|
const basePath = location.pathname;
|
||||||
searchParams.delete('page');
|
searchParams.delete('page');
|
||||||
searchParams.set('order', order);
|
searchParams.set('order', order);
|
||||||
const searchStr = searchParams.toString();
|
const searchStr = searchParams.toString();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { FC, memo } from 'react';
|
import { FC, memo } from 'react';
|
||||||
import { Badge, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
import { Badge, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { Avatar, Icon } from '@answer/components';
|
import { Avatar, Icon } from '@answer/components';
|
||||||
import type { UserInfoRes } from '@answer/common/interface';
|
import type { UserInfoRes } from '@answer/common/interface';
|
||||||
|
@ -16,14 +17,26 @@ const Index: FC<Props> = ({ data }) => {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="d-flex mb-4">
|
<div className="d-flex mb-4">
|
||||||
<a href={`/users/${data.username}`}>
|
{data?.status !== 'deleted' ? (
|
||||||
|
<Link to={`/users/${data.username}`} reloadDocument>
|
||||||
<Avatar avatar={data.avatar} size="160px" />
|
<Avatar avatar={data.avatar} size="160px" />
|
||||||
</a>
|
</Link>
|
||||||
|
) : (
|
||||||
|
<Avatar avatar={data.avatar} size="160px" />
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="ms-4">
|
<div className="ms-4">
|
||||||
<div className="d-flex align-items-center mb-2">
|
<div className="d-flex align-items-center mb-2">
|
||||||
<a href={`/users/${data.username}`} className="text-body h3 mb-0">
|
{data?.status !== 'deleted' ? (
|
||||||
|
<Link
|
||||||
|
to={`/users/${data.username}`}
|
||||||
|
className="text-body h3 mb-0"
|
||||||
|
reloadDocument>
|
||||||
{data.display_name}
|
{data.display_name}
|
||||||
</a>
|
</Link>
|
||||||
|
) : (
|
||||||
|
<span className="text-body h3 mb-0">{data.display_name}</span>
|
||||||
|
)}
|
||||||
{data?.is_admin && (
|
{data?.is_admin && (
|
||||||
<div className="ms-2">
|
<div className="ms-2">
|
||||||
<OverlayTrigger
|
<OverlayTrigger
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Form, Button } from 'react-bootstrap';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import type { FormDataType } from '@answer/common/interface';
|
import type { FormDataType } from '@answer/common/interface';
|
||||||
import { noticeSet, getUserInfo } from '@answer/api';
|
import { setNotice, getUserInfo } from '@answer/api';
|
||||||
import { useToast } from '@answer/hooks';
|
import { useToast } from '@answer/hooks';
|
||||||
|
|
||||||
const Index = () => {
|
const Index = () => {
|
||||||
|
@ -34,7 +34,7 @@ const Index = () => {
|
||||||
const handleSubmit = (event: FormEvent) => {
|
const handleSubmit = (event: FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
noticeSet({
|
setNotice({
|
||||||
notice_switch: formData.notice_switch.value,
|
notice_switch: formData.notice_switch.value,
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
toast.onShow({
|
toast.onShow({
|
||||||
|
|
|
@ -9,7 +9,7 @@ const Suspended = () => {
|
||||||
const userInfo = userInfoStore((state) => state.user);
|
const userInfo = userInfoStore((state) => state.user);
|
||||||
|
|
||||||
if (userInfo.status !== 'forbidden') {
|
if (userInfo.status !== 'forbidden') {
|
||||||
window.location.href = '/';
|
window.location.replace('/');
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ export const readNotification = (id) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useQueryNotificationRedDot = () => {
|
export const useQueryNotificationStatus = () => {
|
||||||
const apiUrl = '/answer/api/v1/notification/status';
|
const apiUrl = '/answer/api/v1/notification/status';
|
||||||
|
|
||||||
return useSWR<{ inbox: number; achievement: number }>(
|
return useSWR<{ inbox: number; achievement: number }>(
|
||||||
|
@ -41,13 +41,13 @@ export const useQueryNotificationRedDot = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearNotificationRedDot = (type) => {
|
export const clearNotificationStatus = (type) => {
|
||||||
return request.instance.put('/answer/api/v1/notification/status', {
|
return request.instance.put('/answer/api/v1/notification/status', {
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clearUnReadNotification = (type) => {
|
export const clearUnreadNotification = (type) => {
|
||||||
return request.instance.put('/answer/api/v1/notification/read/state/all', {
|
return request.instance.put('/answer/api/v1/notification/read/state/all', {
|
||||||
type,
|
type,
|
||||||
});
|
});
|
||||||
|
|
|
@ -96,11 +96,11 @@ export const logout = () => {
|
||||||
return request.get('/answer/api/v1/user/logout');
|
return request.get('/answer/api/v1/user/logout');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const emailVerify = (code: string) => {
|
export const verifyEmail = (code: string) => {
|
||||||
return request.get(`/answer/api/v1/email/verify?code=${code}`);
|
return request.get(`/answer/api/v1/email/verify?code=${code}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const emailReSend = (params?: Type.ImgCodeReq) => {
|
export const resendEmail = (params?: Type.ImgCodeReq) => {
|
||||||
params = qs.parse(
|
params = qs.parse(
|
||||||
qs.stringify(params, {
|
qs.stringify(params, {
|
||||||
skipNulls: true,
|
skipNulls: true,
|
||||||
|
@ -119,7 +119,7 @@ export const getUserInfo = () => {
|
||||||
return request.get<Type.UserInfoRes>('/answer/api/v1/user/info');
|
return request.get<Type.UserInfoRes>('/answer/api/v1/user/info');
|
||||||
};
|
};
|
||||||
|
|
||||||
export const modifyPassword = (params: Type.ModifyPassReq) => {
|
export const modifyPassword = (params: Type.ModifyPasswordReq) => {
|
||||||
return request.post('/answer/api/v1/user/password/modify', params);
|
return request.post('/answer/api/v1/user/password/modify', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -131,15 +131,15 @@ export const uploadAvatar = (params: Type.AvatarUploadReq) => {
|
||||||
return request.post('/answer/api/v1/user/avatar/upload', params);
|
return request.post('/answer/api/v1/user/avatar/upload', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const passRetrieve = (params: Type.PssRetReq) => {
|
export const resetPassword = (params: Type.PasswordResetReq) => {
|
||||||
return request.post('/answer/api/v1/user/password/reset', params);
|
return request.post('/answer/api/v1/user/password/reset', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const passRetrieveSet = (params: { code: string; pass: string }) => {
|
export const replacementPassword = (params: { code: string; pass: string }) => {
|
||||||
return request.post('/answer/api/v1/user/password/replacement', params);
|
return request.post('/answer/api/v1/user/password/replacement', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const accountActivate = (code: string) => {
|
export const activateAccount = (code: string) => {
|
||||||
return request.post(`/answer/api/v1/user/email/verification`, { code });
|
return request.post(`/answer/api/v1/user/email/verification`, { code });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ export const checkImgCode = (params: Type.CheckImgReq) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const noticeSet = (params: Type.NoticeSetReq) => {
|
export const setNotice = (params: Type.SetNoticeReq) => {
|
||||||
return request.post('/answer/api/v1/user/notice/set', params);
|
return request.post('/answer/api/v1/user/notice/set', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -158,7 +158,9 @@ export const saveQuestion = (params: Type.QuestionParams) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const questionDetail = (id: string) => {
|
export const questionDetail = (id: string) => {
|
||||||
return request.get<Type.QuDetailRes>(`/answer/api/v1/question/info?id=${id}`);
|
return request.get<Type.QuestionDetailRes>(
|
||||||
|
`/answer/api/v1/question/info?id=${id}`,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const langConfig = () => {
|
export const langConfig = () => {
|
||||||
|
@ -227,11 +229,11 @@ export const postReport = (params: {
|
||||||
return request.post('/answer/api/v1/report', params);
|
return request.post('/answer/api/v1/report', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const questionDelete = (params: { id: string }) => {
|
export const deleteQuestion = (params: { id: string }) => {
|
||||||
return request.delete('/answer/api/v1/question', params);
|
return request.delete('/answer/api/v1/question', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const answerDelete = (params: { id: string }) => {
|
export const deleteAnswer = (params: { id: string }) => {
|
||||||
return request.delete('/answer/api/v1/answer', params);
|
return request.delete('/answer/api/v1/answer', params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ function isLogin(needToLogin?: boolean): boolean {
|
||||||
// login and active
|
// login and active
|
||||||
if (user.username && user.mail_status === 1) {
|
if (user.username && user.mail_status === 1) {
|
||||||
if (LOGIN_NEED_BACK.includes(path)) {
|
if (LOGIN_NEED_BACK.includes(path)) {
|
||||||
window.location.href = '/';
|
window.location.replace('/');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,6 @@ import { userInfoStore, toastStore } from '@answer/stores';
|
||||||
|
|
||||||
import Storage from './storage';
|
import Storage from './storage';
|
||||||
|
|
||||||
// type Result<T> = {
|
|
||||||
// code: number;
|
|
||||||
// msg: string;
|
|
||||||
// data: T;
|
|
||||||
// };
|
|
||||||
|
|
||||||
const API = {
|
const API = {
|
||||||
development: '',
|
development: '',
|
||||||
production: '',
|
production: '',
|
||||||
|
|
Loading…
Reference in New Issue