d%ri>NneS*QJ1&T6aCt|`UVZaQk~=^^K;ik3`j0~-bgyEhCL
z3T7#*bthJ6#!G<9e84X2)Xmbz5s1$<;?{bI;(62|260G`$o`q>J7%U19yoZ<-Wk4o
zdUpTxjC_3N_>pPl*t9?FI-(svc;v2`***Jbre^oeOsmHa&hFWJDjq0@nd)=
F{sY^{kHP=|
delta 641
zcmY*VF>6y%6#mY;H)---v`x_vw1_5BN_}+bpi7H_G-8*I6)ET=whb}Q1}rt55}ibf
z6yD<0#Xk_lQ9;DP!NI{v930vkhfW0@T%O;3FD-Z<-#h1i=bZ1Hdp^v6sqS9tmKSdV
zvIF3LJq+5jJJ%ipPMMfoZ*8v~Tz_8$^c}!mX{-hh{JZ7ARZIHWKb3;&LP0
z?D%VOOua+i{nkc37@aCUWBm=@%VE%I%Xxfbocg%!0ESaxZd7)y!o+y
z;p|L1^dG+r{E+Q
zG%NPUVh)v@EqUowoQ|rgPkK?m$RIQGrg%^&d*X-^Eg8wmh~y+Mu6R;FbnC*Ce>@?9
A4*&oF
diff --git a/src/assets/iconfont/iconfont.woff b/src/assets/iconfont/iconfont.woff
index fe04cbf8bc18a8ee74c403ee81c97552becdeee0..df31f2c589d9b82c875731ac7ea7a2e6ce7864db 100644
GIT binary patch
delta 4883
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
FMsL_29CrW!
delta 2700
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
G7XSb%90(%-
diff --git a/src/assets/iconfont/iconfont.woff2 b/src/assets/iconfont/iconfont.woff2
index dcd2c95cfe5024573ce68e00c6a278659bde47a4..e19a3b4636128e87ab531e848b0774864ade8694 100644
GIT binary patch
literal 4516
zcmV;V5nJwePew8T0RR9101>1B3jhEB03N^q01;LI0RR9100000000000000000000
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
C393X9s~W8zK**qE2#BFxg)WIAss(@#;Lg
zUAIR0_72+(M`EFdiOQ(V)KmF(VzM%R6TiA3Wzm;PaK3Z)Pdhs7u!1h)HKi!Bc%3PvLd
zh#MX^?O(t{>yZkg6IzF=Q>aXk&1*Jwa_8ahYXX(cE6C=xS@le6z?pXEP7e?yUH_jo
zSM`=hBjKAjdO1V>-EL+=GD$odgeV$GCK{<^9-&e)Aw+t|#GLk|4p?>o`L|pL0jVL!
zX4m!Oi`Z;n5_{&!a6ynQZ4HR!Hx*M*E`rom-8K_BU_w3h3fOnPUcQl72E`8KAmepa
z2;n|0Gaqy?t$_&r3M`_#Hnh9~ETDnDiD~4n*mME32Yn8mW)&KiL8bDyv#Ol69JGur
z3zyZ)<;#`_9f1HjGs!CJNMb^zYE@htf&{6$`%&rzD5vsc%)7yLdAi|wDza8r(VjT{Fz~+I7#{v8BTE_s5(l*@T7(y%Q
z9)tqX^}HC3_E@9LqcaeJXH$riRNJvk8pJ6rWkp8@TnNc>;2zs*8qKkf>EaoEgppkE
zFWTn4b7smX+9q&;1G6yj~EOHjzt9
zR@Y^y<00#*v(RK$FxhCJ
z;`FO~uULfAFsH@^i1W#JC$B+F!r`^Pby3~oCQZ%sMT5E0;AF6WDsH~!Dqgw%gJ*)9
zl{4vI3*fB|+YXpiyYu&}4|JpycrhEDn+}{j{0(A<{pZHo44H5y{yCpsWr#Bvn)Z_zE)-zZ2g
zEgl34j+h(4~Y%9|1W9Zs5NUeX6?oW?TTWrLu(i4!{r|_>aSM0qOS?t
z-yeVR+0MYH7Zcdw>KJu6rOhMCnw}fs!NX*CYv>U{7gzr@hg(wo-13DVDkBXb%X*>a
zk4=giS=HfoeHavDN-K+jT=|jq&-LXsoBpT?Z;`cz{}vrrRL-8#_48^$_Q#UmE7#R}y~q06wNLi`CD=E3O)R@V@t2m#^*A=VDFowLF;Er8mHXNoaJ7CQViF5Gt?k
z(&wDR00N&DAJm$+=TB^^N!-ahn%CM~_$&nESsV4yusm^R+WSxBo3|g-78!I0)%{>-f5?%M@TBRZtd~@
zp44qydxO#x?I;4l@JErMAgKg|iX&V`~Acf>kc#?v~Z5G^$~2TZk%n8#b)ctk@nk
zEsu>ZXCGo$$KH=vESxy>NYTpL$wMXCoN85xsywnMayzGmJ*;?JE>TJ4FDr)G2ibKI
zuE=uvOeC+CldYLNB!>Z~M5wr|D>9^4-uUK;T^ZZbw`@+^p7EYWZPZ7@``NWFe}C@o
zga1A7m`XIHwWMk+ZOpc4+DPwHrh?XDS3SX>d*S@u`}2R^8!9|naj@}=KZ~SLor6fR
zkL(L7$?i`$(HR;XZb&Fis`$r!sJc7nzYTwEs{8WaeF1ge7xYDI9kdW3(ePIwqrrIm
z?Y+L}x6a4>=Q-A)8u3(+I7TF#5?407@<`zUaOx9CLSWLgE^=z_o6*J0R8&5W<<$We
zhyhK21({!}2Y|p+)iO_Vja4tx8u~*!lUHOp#2CIOFULE-?1$~4xpn*;vj5ZG-(fC>
ziS9~k>2SX#Die3$xt-Mi0nC0=K^L7Es{ep9KxGjP>oU*6;oU>32ElFDR_TYBs>3uyrZq=#c=M!*3Xq-JAdf0>R@7aoDJkoiPf>p>Jb@elQuc
zmfE{kUca0So5sn`y9yOYC3CkQ$2x0+NV!&A@DN~2ups3o4K2kXdSH%zzbD8D8o*a9
zelvJI<1~9I6-g=--_TU4!-*9321bU-ph*KfLF+NsKL*sOkRx^3OI47XJ1SM
z>8f-zJky?Rnse8woI498T8vn6;w4BTsbo^1>;jo-8T1B@%-GVZ2|$gVSk#)o$Rvzh
zi?qeA49lK%eOe+Jg&It-yDyNxk1b50sBp0_U6|lPk&xRZs}QIRusm^ron(Xn0001<
Ck_nLj
diff --git a/src/components/WangEidtor/WangEidtor.tsx b/src/components/WangEidtor/WangEidtor.tsx
new file mode 100644
index 0000000..d4a4f60
--- /dev/null
+++ b/src/components/WangEidtor/WangEidtor.tsx
@@ -0,0 +1,129 @@
+import React, { useState, useEffect, forwardRef } from 'react';
+import '@wangeditor/editor/dist/css/style.css';
+import { Editor, Toolbar } from '@wangeditor/editor-for-react';
+import {
+ IDomEditor,
+ IEditorConfig,
+ IToolbarConfig,
+ i18nChangeLanguage,
+ i18nAddResources,
+} from '@wangeditor/editor';
+
+// 定义多语言组件属性
+export interface WangEditorProps {
+ value?: string;
+ onChange?: (html: string) => void;
+ language?: 'zh-CN' | 'en';
+ height?: string;
+ placeholder?: string;
+ mode?: 'default' | 'simple';
+ readOnly?: boolean;
+ toolbarKeys?: string[];
+}
+
+// 添加全局多语言配置
+i18nAddResources('zh-CN', {
+ editor: {
+ placeholder: '请输入内容...',
+ },
+});
+
+i18nAddResources('en', {
+ editor: {
+ placeholder: 'Please enter content...',
+ },
+});
+
+// 使用forwardRef以便能在Form.Item中使用ref
+const WangEditor = forwardRef(({
+ value = '',
+ onChange,
+ language = 'zh-CN',
+ height = '300px',
+ placeholder,
+ mode = 'default',
+ readOnly = false,
+ toolbarKeys,
+}, ref) => {
+ // 编辑器实例
+ const [editor, setEditor] = useState(null);
+ // HTML内容
+ const [html, setHtml] = useState(value);
+
+ // 设置语言
+ useEffect(() => {
+ i18nChangeLanguage(language);
+ }, [language]);
+
+ // 工具栏配置
+ const toolbarConfig: Partial = {
+ excludeKeys: [],
+ };
+
+ if (toolbarKeys) {
+ toolbarConfig.toolbarKeys = toolbarKeys;
+ }
+
+ // 编辑器配置
+ const editorConfig: Partial = {
+ placeholder: placeholder || (language === 'zh-CN' ? '请输入内容...' : 'Please enter content...'),
+ readOnly,
+ };
+
+ // 监听value变化
+ useEffect(() => {
+ if (editor && value !== html) {
+ setHtml(value);
+ }
+ }, [value, editor]);
+
+ // 及时销毁editor实例,重要!
+ useEffect(() => {
+ return () => {
+ if (editor == null) return;
+ editor.destroy();
+ setEditor(null);
+ };
+ }, [editor]);
+
+ // 处理编辑器内容变化
+ const handleChange = (newEditor: IDomEditor) => {
+ const newHtml = newEditor.getHtml();
+ setHtml(newHtml);
+
+ // 调用Form.Item的onChange回调,支持表单验证
+ if (onChange) {
+ // 如果内容为空或只有空段落,传递空字符串以触发验证
+ if (!newHtml || newHtml === '' || newHtml === '
') {
+ onChange('');
+ } else {
+ onChange(newHtml);
+ }
+ }
+ };
+
+ return (
+
+
+ {!readOnly && (
+
+ )}
+
+
+
+ );
+});
+
+export default WangEditor;
diff --git a/src/layouts/Language.tsx b/src/layouts/Language.tsx
index 221b121..a289e70 100644
--- a/src/layouts/Language.tsx
+++ b/src/layouts/Language.tsx
@@ -1,10 +1,10 @@
-import React, { useState } from 'react';
-import { getLocale,setLocale } from 'umi';
+import React, { useState, useEffect } from 'react';
+import { getLocale, setLocale } from 'umi';
import './Language.less'
const Language: React.FC = (props) => {
- const locale = getLocale();
+ const [currentLocale, setCurrentLocale] = useState(getLocale());
const [languageList, setLanguageList] = useState([
{
label: '中',
@@ -15,12 +15,32 @@ const Language: React.FC = (props) => {
value: 'en-US',
},
]);
+
+ // 处理语言切换
+ const handleChangeLanguage = (value: string) => {
+ setLocale(value, false);
+ setCurrentLocale(value);
+ };
+
+ // 组件挂载时确保当前语言状态正确
+ useEffect(() => {
+ const locale = getLocale();
+ setCurrentLocale(locale);
+ }, []);
+
return (
{languageList.map((item) => (
- setLocale(item.value, false)} className={item.value === locale ? 'active' : ''} key={item.value}>{item.label}
+ handleChangeLanguage(item.value)}
+ className={item.value === currentLocale ? 'active' : ''}
+ key={item.value}
+ >
+ {item.label}
+
))}
);
};
+
export default Language;
diff --git a/src/layouts/SiderMenu.tsx b/src/layouts/SiderMenu.tsx
index 5761c54..d7f8f36 100644
--- a/src/layouts/SiderMenu.tsx
+++ b/src/layouts/SiderMenu.tsx
@@ -5,8 +5,9 @@ import IconFont from '@/components/IconFont/IconFont';
interface IMenuItem {
label: string;
key: string;
- path: string;
+ path?: string;
icon: string;
+ children?: IMenuItem[];
}
// 引入样式文件 useIntl().formatMessage({ id: 'menu.首页' }),
const items: IMenuItem[] = [
@@ -50,7 +51,46 @@ const items: IMenuItem[] = [
label: 'menu.帮助中心管理',
key: 'helpManage',
path: '/helpManage',
- icon: 'icon-bangzhuzhongxin',
+ icon: 'icon-bangzhuzhongxin'
+ },
+ {
+ label: 'menu.用户提问管理',
+ key: 'userQuestionManage',
+ icon: 'icon-yonghutiwen',
+ path: '/userQuestionManage',
+ children: [
+ {
+ label: 'menu.已阅问题',
+ key: 'readQuestionManage',
+ icon: 'icon-yiyue',
+ path: '/readQuestionManage',
+ },
+ {
+ label: 'menu.未阅问题',
+ key: 'unreadQuestionManage',
+ icon: 'icon-weiyuedu',
+ path: '/unreadQuestionManage',
+ },
+ ],
+ },
+ {
+ label: 'menu.友情链接管理',
+ key: 'friendLinkManage',
+ icon: 'icon-youqinglianjie',
+ children: [
+ {
+ label: 'menu.友情链接分类',
+ key: 'friendLinkCategory',
+ icon: 'icon-fenlei',
+ path: '/friendLinkCategory',
+ },
+ {
+ label: 'menu.友情链接列表',
+ key: 'friendLinkList',
+ icon: 'icon-liebiaomoshi',
+ path: '/friendLinkManage',
+ },
+ ],
},
];
@@ -73,13 +113,26 @@ const SiderMenu: React.FC = (props) => {
}, [history.location.pathname]);
return (
-
);
diff --git a/src/locales/en-US.ts b/src/locales/en-US.ts
index d055e39..c72e7bb 100644
--- a/src/locales/en-US.ts
+++ b/src/locales/en-US.ts
@@ -6,6 +6,12 @@ export default {
'menu.政策法规管理': 'Policy Management',
'menu.关于我们管理': 'About Us Management',
'menu.帮助中心管理': 'Help Center Management',
+ 'menu.用户提问管理': 'User Questions Management',
+ 'menu.已阅问题': 'Read Questions',
+ 'menu.未阅问题': 'Unread Questions',
+ 'menu.友情链接管理': 'Friendly Links Management',
+ 'menu.友情链接分类': 'Friendly Links Category',
+ 'menu.友情链接列表': 'Friendly Links List',
// About page
'about.title': 'About Us',
@@ -95,5 +101,115 @@ export default {
"register.expert.idType.required": "Please select ID type",
"register.expert.idCard.label": "ID Number",
"register.expert.idCard.placeholder": "Please enter ID number",
- "register.expert.idCard.required": "Please enter ID number"
+ "register.expert.idCard.required": "Please enter ID number",
+
+ // Help Center Management
+ 'helpCenter.title': 'Help Center Management',
+ 'helpCenter.question': 'Question',
+ 'helpCenter.category': 'Category',
+ 'helpCenter.createTime': 'Create Time',
+ 'helpCenter.updateTime': 'Update Time',
+ 'helpCenter.status': 'Status',
+ 'helpCenter.operation': 'Operation',
+ 'helpCenter.edit': 'Edit',
+ 'helpCenter.delete': 'Delete',
+ 'helpCenter.add': 'Add',
+ 'helpCenter.search': 'Search',
+ 'helpCenter.all': 'All',
+ 'helpCenter.published': 'Published',
+ 'helpCenter.unpublished': 'Unpublished',
+ 'helpCenter.draft': 'Draft',
+ 'helpCenter.publish': 'Publish',
+ 'helpCenter.unpublish': 'Unpublish',
+ 'helpCenter.confirmDelete': 'Are you sure you want to delete this help item?',
+ 'helpCenter.batchDelete': 'Are you sure you want to delete selected help items?',
+
+ // User Questions Management page
+ 'userQuestion.title': 'User Questions Management',
+ 'userQuestion.user': 'User',
+ 'userQuestion.question': 'Question',
+ 'userQuestion.answer': 'Answer',
+ 'userQuestion.createTime': 'Asked At',
+ 'userQuestion.status': 'Status',
+ 'userQuestion.operation': 'Operation',
+ 'userQuestion.unanswered': 'Unanswered',
+ 'userQuestion.answered': 'Answered',
+ 'userQuestion.reply': 'Reply',
+ 'userQuestion.view': 'View',
+ 'userQuestion.viewAll': 'View All',
+ 'userQuestion.confirmDelete': 'Are you sure you want to delete this question?',
+ 'userQuestion.batchDelete': 'Are you sure you want to delete the selected questions?',
+ 'userQuestion.description': 'User Questions Management Instructions',
+ 'userQuestion.descriptionText': 'The User Questions Management module is used to process questions submitted by users on the platform. Read questions are questions that have been replied to, and unread questions are questions waiting to be replied to. You can click on the respective card to enter the corresponding management page.',
+ 'userQuestion.deleteSuccess': 'Successfully deleted',
+ 'userQuestion.deleteFailed': 'Failed to delete',
+ 'userQuestion.replySuccess': 'Successfully replied',
+ 'userQuestion.replyFailed': 'Failed to reply',
+ 'userQuestion.selectRequired': 'Please select at least one item',
+ 'userQuestion.batchDeleteConfirm': 'This action cannot be undone. Are you sure you want to continue?',
+
+ // Read Questions page
+ 'readQuestion.title': 'Read Questions Management',
+ 'readQuestion.user': 'Username',
+ 'readQuestion.question': 'Question Content',
+ 'readQuestion.answer': 'Reply Content',
+ 'readQuestion.createTime': 'Asked Time',
+ 'readQuestion.answerTime': 'Reply Time',
+ 'readQuestion.status': 'Status',
+ 'readQuestion.operation': 'Operation',
+ 'readQuestion.view': 'View Details',
+ 'readQuestion.delete': 'Delete',
+ 'readQuestion.search': 'Search',
+ 'readQuestion.reset': 'Reset',
+ 'readQuestion.confirmDelete': 'Are you sure you want to delete this question?',
+ 'readQuestion.batchDelete': 'Are you sure you want to delete the selected questions?',
+ 'readQuestion.viewDetails': 'View Question Details',
+ 'readQuestion.questionDetails': 'Question Details',
+ 'readQuestion.answerDetails': 'Reply Details',
+ 'readQuestion.close': 'Close',
+ 'readQuestion.deleteSuccess': 'Successfully deleted',
+ 'readQuestion.deleteFailed': 'Failed to delete',
+ 'readQuestion.selectRequired': 'Please select at least one item',
+ 'readQuestion.batchDeleteConfirm': 'This action cannot be undone. Are you sure you want to continue?',
+ 'readQuestion.questionPlaceholder': 'Please enter question keywords',
+ 'readQuestion.fetchFailed': 'Failed to fetch answered questions',
+
+ // Unread Questions page
+ 'unreadQuestion.title': 'Unread Questions Management',
+ 'unreadQuestion.user': 'Username',
+ 'unreadQuestion.question': 'Question Content',
+ 'unreadQuestion.createTime': 'Asked Time',
+ 'unreadQuestion.status': 'Status',
+ 'unreadQuestion.operation': 'Operation',
+ 'unreadQuestion.reply': 'Reply',
+ 'unreadQuestion.delete': 'Delete',
+ 'unreadQuestion.search': 'Search',
+ 'unreadQuestion.reset': 'Reset',
+ 'unreadQuestion.confirmDelete': 'Are you sure you want to delete this question?',
+ 'unreadQuestion.batchDelete': 'Are you sure you want to delete the selected questions?',
+ 'unreadQuestion.replyModal': 'Reply to Question',
+ 'unreadQuestion.questionContent': 'Question Content',
+ 'unreadQuestion.answerContent': 'Reply Content',
+ 'unreadQuestion.answerPlaceholder': 'Please enter your reply...',
+ 'unreadQuestion.submit': 'Submit',
+ 'unreadQuestion.cancel': 'Cancel',
+ 'unreadQuestion.answerRequired': 'Please enter reply content',
+ 'unreadQuestion.selectRequired': 'Please select at least one item',
+ 'unreadQuestion.batchDeleteConfirm': 'This action cannot be undone. Are you sure you want to continue?',
+ 'unreadQuestion.questionPlaceholder': 'Please enter question keywords',
+ 'unreadQuestion.fetchFailed': 'Failed to fetch unanswered questions',
+
+ // Friendly Links Management page
+ 'friendLink.title': 'Friendly Links Management',
+ 'friendLink.name': 'Name',
+ 'friendLink.url': 'URL',
+ 'friendLink.logo': 'Logo',
+ 'friendLink.sort': 'Sort Order',
+ 'friendLink.status': 'Status',
+ 'friendLink.operation': 'Operation',
+ 'friendLink.add': 'Add Link',
+ 'friendLink.edit': 'Edit Link',
+ 'friendLink.delete': 'Delete Link',
+ 'friendLink.enabled': 'Enabled',
+ 'friendLink.disabled': 'Disabled'
};
diff --git a/src/locales/zh-CN.ts b/src/locales/zh-CN.ts
index 84c89bb..7385e14 100644
--- a/src/locales/zh-CN.ts
+++ b/src/locales/zh-CN.ts
@@ -6,6 +6,123 @@ export default {
'menu.政策法规管理': '政策法规管理',
'menu.关于我们管理': '关于我们管理',
'menu.帮助中心管理': '帮助中心管理',
+ 'menu.用户提问管理': '用户提问管理',
+ 'menu.已阅问题': '已阅问题',
+ 'menu.未阅问题': '未阅问题',
+ 'menu.友情链接管理': '友情链接管理',
+ 'menu.友情链接分类': '友情链接分类',
+ 'menu.友情链接列表': '友情链接列表',
+
+
+ // 帮助中心页面
+ 'helpCenter.title': '帮助中心管理',
+ 'helpCenter.question': '问题',
+ 'helpCenter.category': '问题类型',
+ 'helpCenter.createTime': '创建时间',
+ 'helpCenter.updateTime': '更新时间',
+ 'helpCenter.status': '状态',
+ 'helpCenter.operation': '操作',
+ 'helpCenter.edit': '编辑',
+ 'helpCenter.delete': '删除',
+ 'helpCenter.add': '新增',
+ 'helpCenter.search': '搜索',
+ 'helpCenter.all': '全部',
+ 'helpCenter.published': '已发布',
+ 'helpCenter.unpublished': '未发布',
+ 'helpCenter.draft': '草稿',
+ 'helpCenter.publish': '发布',
+ 'helpCenter.unpublish': '下架',
+ 'helpCenter.confirmDelete': '确定要删除该帮助吗?',
+ 'helpCenter.batchDelete': '确定要批量删除选中的帮助吗?',
+
+ // 用户提问管理页面
+ 'userQuestion.title': '用户提问管理',
+ 'userQuestion.user': '用户',
+ 'userQuestion.question': '问题',
+ 'userQuestion.answer': '回答',
+ 'userQuestion.createTime': '提问时间',
+ 'userQuestion.status': '状态',
+ 'userQuestion.operation': '操作',
+ 'userQuestion.unanswered': '未回答',
+ 'userQuestion.answered': '已回答',
+ 'userQuestion.reply': '回复',
+ 'userQuestion.view': '查看',
+ 'userQuestion.viewAll': '查看全部',
+ 'userQuestion.confirmDelete': '确定要删除该问题吗?',
+ 'userQuestion.batchDelete': '确定要批量删除选中的问题吗?',
+ 'userQuestion.description': '用户提问管理说明',
+ 'userQuestion.descriptionText': '用户提问管理模块用于处理用户在平台上提交的问题。已阅问题为已回复的问题,未阅问题为待回复的问题。您可以点击相应的卡片进入对应的管理页面。',
+ 'userQuestion.deleteSuccess': '删除成功',
+ 'userQuestion.deleteFailed': '删除失败',
+ 'userQuestion.replySuccess': '回复成功',
+ 'userQuestion.replyFailed': '回复失败',
+ 'userQuestion.selectRequired': '请至少选择一项',
+ 'userQuestion.batchDeleteConfirm': '删除后无法恢复,确定要继续吗?',
+
+ // 已阅问题页面
+ 'readQuestion.title': '已阅问题管理',
+ 'readQuestion.user': '用户名',
+ 'readQuestion.question': '问题内容',
+ 'readQuestion.answer': '回复内容',
+ 'readQuestion.createTime': '提问时间',
+ 'readQuestion.answerTime': '回复时间',
+ 'readQuestion.status': '状态',
+ 'readQuestion.operation': '操作',
+ 'readQuestion.view': '查看详情',
+ 'readQuestion.delete': '删除',
+ 'readQuestion.search': '搜索',
+ 'readQuestion.reset': '重置',
+ 'readQuestion.confirmDelete': '确定要删除该问题吗?',
+ 'readQuestion.batchDelete': '确定要批量删除选中的问题吗?',
+ 'readQuestion.viewDetails': '查看问题详情',
+ 'readQuestion.questionDetails': '问题详情',
+ 'readQuestion.answerDetails': '回复详情',
+ 'readQuestion.close': '关闭',
+ 'readQuestion.deleteSuccess': '删除成功',
+ 'readQuestion.deleteFailed': '删除失败',
+ 'readQuestion.selectRequired': '请至少选择一项',
+ 'readQuestion.batchDeleteConfirm': '删除后无法恢复,确定要继续吗?',
+ 'readQuestion.questionPlaceholder': '请输入问题关键词',
+ 'readQuestion.fetchFailed': '获取已回答问题列表失败',
+
+ // 未阅问题页面
+ 'unreadQuestion.title': '未阅问题管理',
+ 'unreadQuestion.user': '用户名',
+ 'unreadQuestion.question': '问题内容',
+ 'unreadQuestion.createTime': '提问时间',
+ 'unreadQuestion.status': '状态',
+ 'unreadQuestion.operation': '操作',
+ 'unreadQuestion.reply': '回复',
+ 'unreadQuestion.delete': '删除',
+ 'unreadQuestion.search': '搜索',
+ 'unreadQuestion.reset': '重置',
+ 'unreadQuestion.confirmDelete': '确定要删除该问题吗?',
+ 'unreadQuestion.batchDelete': '确定要批量删除选中的问题吗?',
+ 'unreadQuestion.replyModal': '回复问题',
+ 'unreadQuestion.questionContent': '问题内容',
+ 'unreadQuestion.answerContent': '回复内容',
+ 'unreadQuestion.answerPlaceholder': '请输入回复内容...',
+ 'unreadQuestion.submit': '提交',
+ 'unreadQuestion.cancel': '取消',
+ 'unreadQuestion.answerRequired': '请输入回复内容',
+ 'unreadQuestion.selectRequired': '请至少选择一项',
+ 'unreadQuestion.batchDeleteConfirm': '删除后无法恢复,确定要继续吗?',
+ 'unreadQuestion.questionPlaceholder': '请输入问题关键词',
+ 'unreadQuestion.fetchFailed': '获取未回答问题列表失败',
+
+ // 友情链接管理页面
+ 'friendLink.title': '友情链接管理',
+ 'friendLink.name': '名称',
+ 'friendLink.url': '链接地址',
+ 'friendLink.logo': '图标',
+ 'friendLink.sort': '排序',
+ 'friendLink.status': '状态',
+ 'friendLink.operation': '操作',
+ 'friendLink.add': '新增链接',
+ 'friendLink.edit': '编辑链接',
+ 'friendLink.delete': '删除链接',
+ 'friendLink.enabled': '已启用',
+ 'friendLink.disabled': '已禁用',
// 关于我们页面
'about.title': '关于我们',
diff --git a/src/pages/aboutManage/aboutManage.less b/src/pages/aboutManage/aboutManage.less
index eb75d89..ac5d200 100644
--- a/src/pages/aboutManage/aboutManage.less
+++ b/src/pages/aboutManage/aboutManage.less
@@ -8,4 +8,27 @@
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
+
+ .ant-card {
+ margin-bottom: 16px;
+ }
+
+ .ant-form-item-label > label {
+ font-weight: 500;
+ }
+
+ .save-btn-wrapper {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 16px;
+ }
+
+ .ant-tabs-nav {
+ margin-bottom: 16px;
+ }
+
+ .ant-card-head-title {
+ font-size: 18px;
+ font-weight: 500;
+ }
}
diff --git a/src/pages/aboutManage/aboutManage.tsx b/src/pages/aboutManage/aboutManage.tsx
index 3f11bbd..82ff7fd 100644
--- a/src/pages/aboutManage/aboutManage.tsx
+++ b/src/pages/aboutManage/aboutManage.tsx
@@ -1,16 +1,249 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import { useIntl } from 'umi';
+import { Card, Form, Input, Button, message, Tabs, Spin } from 'antd';
+import WangEditor from '@/components/WangEidtor/WangEidtor';
+import { getAboutUs, updateAboutUs, AboutUsRequest } from '@/servers/api/about';
import './aboutManage.less';
+const { TabPane } = Tabs;
+
const AboutManage: React.FC = () => {
const intl = useIntl();
+ const [form] = Form.useForm();
+ const [loading, setLoading] = useState(false);
+ const [submitting, setSubmitting] = useState(false);
+ const [activeTabKey, setActiveTabKey] = useState('zh');
+ const [aboutData, setAboutData] = useState(null);
+
+ // 获取关于我们详情数据
+ const fetchAboutData = async () => {
+ setLoading(true);
+ try {
+ const response = await getAboutUs();
+ if (response && response.success) {
+ setAboutData(response.data);
+ // 设置表单初始值
+ form.setFieldsValue({
+ title: response.data.title,
+ titleEn: response.data.titleEn,
+ content: response.data.content,
+ contentEn: response.data.contentEn,
+ address: response.data.address,
+ addressEn: response.data.addressEn,
+ contactsPhone: response.data.contactsPhone,
+ contactsPhoneEn: response.data.contactsPhoneEn,
+ contactsEmail: response.data.contactsEmail,
+ contactsEmailEn: response.data.contactsEmailEn,
+ contactsConsult: response.data.contactsConsult,
+ contactsConsultEn: response.data.contactsConsultEn,
+ });
+ } else {
+ message.error(response.message || '获取数据失败');
+ }
+ } catch (error) {
+ console.error('获取关于我们数据失败:', error);
+ message.error('获取数据失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchAboutData();
+ }, []);
+
+ // 处理Tab切换
+ const handleTabChange = (key: string) => {
+ setActiveTabKey(key);
+ };
+
+ // 处理表单提交
+ const handleSubmit = async () => {
+ try {
+ const values = await form.validateFields();
+ setSubmitting(true);
+
+ // 准备提交数据
+ const submitData: AboutUsRequest = {
+ ...aboutData,
+ ...values,
+ };
+
+ // 保留原有的addressImg,如果没有则设置为空字符串
+ submitData.addressImg = aboutData?.addressImg || '';
+
+ const response = await updateAboutUs(submitData);
+ if (response && response.success) {
+ message.success('保存成功');
+ fetchAboutData(); // 刷新数据
+ } else {
+ message.error(response.message || '保存失败');
+ }
+ } catch (errorInfo) {
+ // 获取所有字段的错误信息
+ const errorFields = errorInfo.errorFields || [];
+
+ // 检查是否有中文标题或内容的错误
+ const hasZhError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return !fieldName.includes('En');
+ });
+
+ // 检查是否有英文标题或内容的错误
+ const hasEnError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('En');
+ });
+
+ // 如果有中文字段错误,切换到中文Tab
+ if (hasZhError) {
+ setActiveTabKey('zh');
+ }
+ // 如果只有英文字段错误,切换到英文Tab
+ else if (hasEnError && !hasZhError) {
+ setActiveTabKey('en');
+ }
+
+ console.log('表单验证失败:', errorInfo);
+ } finally {
+ setSubmitting(false);
+ }
+ };
return (
-
-
{intl.formatMessage({ id: 'menu.关于我们管理' })}
-
- {/* 关于我们管理内容 */}
-
+
+
+ 保存
+
+ }
+ >
+
+
+
+
);
};
diff --git a/src/pages/friendLinkManage/friendLinkCategory.tsx b/src/pages/friendLinkManage/friendLinkCategory.tsx
new file mode 100644
index 0000000..1eae7b8
--- /dev/null
+++ b/src/pages/friendLinkManage/friendLinkCategory.tsx
@@ -0,0 +1,409 @@
+import React, { useState, useEffect } from 'react';
+import { useIntl } from 'umi';
+import { Card, Table, Button, Modal, Form, Input, Space, message, Select, TreeSelect } from 'antd';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, EditOutlined } from '@ant-design/icons';
+import './friendLinkManage.less';
+
+const { Option } = Select;
+
+// 友情链接分类类型定义
+interface CategoryType {
+ id: string;
+ name: string;
+ parentId: string | null;
+ level: number;
+ sort: number;
+ children?: CategoryType[];
+ key?: string;
+}
+
+const FriendLinkCategory: React.FC = () => {
+ const intl = useIntl();
+ const [loading, setLoading] = useState
(false);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [isEdit, setIsEdit] = useState(false);
+ const [isAddChild, setIsAddChild] = useState(false);
+ const [currentCategory, setCurrentCategory] = useState(null);
+ const [categoryData, setCategoryData] = useState([]);
+ const [form] = Form.useForm();
+
+ // 模拟分类数据
+ const mockCategories: CategoryType[] = [
+ {
+ id: '1',
+ name: '政府机构',
+ parentId: null,
+ level: 1,
+ sort: 1,
+ children: [
+ {
+ id: '1-1',
+ name: '中央部委',
+ parentId: '1',
+ level: 2,
+ sort: 1,
+ },
+ {
+ id: '1-2',
+ name: '地方政府',
+ parentId: '1',
+ level: 2,
+ sort: 2,
+ children: [
+ {
+ id: '1-2-1',
+ name: '省级政府',
+ parentId: '1-2',
+ level: 3,
+ sort: 1,
+ },
+ {
+ id: '1-2-2',
+ name: '市级政府',
+ parentId: '1-2',
+ level: 3,
+ sort: 2,
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '2',
+ name: '港口集团',
+ parentId: null,
+ level: 1,
+ sort: 2,
+ children: [
+ {
+ id: '2-1',
+ name: '沿海港口',
+ parentId: '2',
+ level: 2,
+ sort: 1,
+ },
+ {
+ id: '2-2',
+ name: '内河港口',
+ parentId: '2',
+ level: 2,
+ sort: 2,
+ }
+ ]
+ },
+ {
+ id: '3',
+ name: '航运企业',
+ parentId: null,
+ level: 1,
+ sort: 3,
+ }
+ ];
+
+ // 获取分类列表
+ const fetchCategoryList = () => {
+ setLoading(true);
+ // 实际项目中应调用API
+ setTimeout(() => {
+ // 为每个节点添加key属性,用于Table组件
+ const processData = (data: CategoryType[]): CategoryType[] => {
+ return data.map(item => {
+ const newItem = { ...item, key: item.id };
+ if (item.children && item.children.length > 0) {
+ newItem.children = processData(item.children);
+ }
+ return newItem;
+ });
+ };
+
+ const processedData = processData(mockCategories);
+ setCategoryData(processedData);
+ setLoading(false);
+ }, 500);
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchCategoryList();
+ }, []);
+
+ // 获取所有分类(扁平化)用于选择父分类
+ const getAllCategories = (categories: CategoryType[] = categoryData, result: CategoryType[] = []): CategoryType[] => {
+ categories.forEach(category => {
+ result.push(category);
+ if (category.children && category.children.length > 0) {
+ getAllCategories(category.children, result);
+ }
+ });
+ return result;
+ };
+
+ // 处理添加分类
+ const handleAddCategory = () => {
+ setIsEdit(false);
+ setIsAddChild(false);
+ setCurrentCategory(null);
+ form.resetFields();
+ setModalVisible(true);
+ };
+
+ // 处理添加子分类
+ const handleAddChildCategory = (record: CategoryType) => {
+ setIsEdit(false);
+ setIsAddChild(true);
+ setCurrentCategory(record);
+ form.resetFields();
+ form.setFieldsValue({
+ parentId: record.id,
+ });
+ setModalVisible(true);
+ };
+
+ // 处理编辑分类
+ const handleEditCategory = (record: CategoryType) => {
+ setIsEdit(true);
+ setIsAddChild(false);
+ setCurrentCategory(record);
+ form.setFieldsValue({
+ name: record.name,
+ parentId: record.parentId,
+ sort: record.sort,
+ });
+ setModalVisible(true);
+ };
+
+ // 处理删除分类
+ const handleDeleteCategory = (record: CategoryType) => {
+ // 检查是否有子分类
+ if (record.children && record.children.length > 0) {
+ message.error('该分类下有子分类,不能直接删除');
+ return;
+ }
+
+ Modal.confirm({
+ title: '删除分类',
+ icon: ,
+ content: `确定要删除分类"${record.name}"吗?`,
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ onOk() {
+ // 实际项目中应调用API
+ // 递归查找并删除分类
+ const deleteCategory = (data: CategoryType[], id: string): CategoryType[] => {
+ return data.filter(item => {
+ if (item.id === id) {
+ return false;
+ }
+ if (item.children && item.children.length > 0) {
+ item.children = deleteCategory(item.children, id);
+ }
+ return true;
+ });
+ };
+
+ const newData = deleteCategory(categoryData, record.id);
+ setCategoryData(newData);
+ message.success('删除成功');
+ },
+ });
+ };
+
+ // 处理表单提交
+ const handleModalSubmit = () => {
+ form.validateFields().then(values => {
+ // 准备提交数据
+ const formData = {
+ ...values,
+ level: values.parentId ? (isAddChild ? currentCategory!.level + 1 : 2) : 1,
+ };
+
+ if (isEdit && currentCategory) {
+ // 编辑模式
+ // 递归更新分类
+ const updateCategory = (data: CategoryType[], id: string, newData: any): CategoryType[] => {
+ return data.map(item => {
+ if (item.id === id) {
+ return { ...item, ...newData };
+ }
+ if (item.children && item.children.length > 0) {
+ item.children = updateCategory(item.children, id, newData);
+ }
+ return item;
+ });
+ };
+
+ const newData = updateCategory(categoryData, currentCategory.id, formData);
+ setCategoryData(newData);
+ message.success('更新成功');
+ } else {
+ // 新增模式
+ const newId = isAddChild
+ ? `${currentCategory!.id}-${Date.now().toString().substr(-4)}`
+ : (categoryData.length + 1).toString();
+
+ const newCategory: CategoryType = {
+ id: newId,
+ name: formData.name,
+ parentId: formData.parentId,
+ level: formData.level,
+ sort: formData.sort || 99,
+ key: newId,
+ };
+
+ if (formData.parentId) {
+ // 有父分类,需要将新分类添加到父分类的children中
+ const addChildToParent = (data: CategoryType[], parentId: string, newChild: CategoryType): CategoryType[] => {
+ return data.map(item => {
+ if (item.id === parentId) {
+ if (!item.children) {
+ item.children = [];
+ }
+ item.children.push(newChild);
+ return item;
+ }
+ if (item.children && item.children.length > 0) {
+ item.children = addChildToParent(item.children, parentId, newChild);
+ }
+ return item;
+ });
+ };
+
+ const newData = addChildToParent(categoryData, formData.parentId, newCategory);
+ setCategoryData(newData);
+ } else {
+ // 无父分类,直接添加到顶级
+ setCategoryData([...categoryData, newCategory]);
+ }
+
+ message.success('添加成功');
+ }
+
+ setModalVisible(false);
+ form.resetFields();
+ });
+ };
+
+ // 表格列定义
+ const columns = [
+ {
+ title: '分类名称',
+ dataIndex: 'name',
+ key: 'name',
+ },
+ {
+ title: '层级',
+ dataIndex: 'level',
+ key: 'level',
+ width: 100,
+ },
+ {
+ title: '排序',
+ dataIndex: 'sort',
+ key: 'sort',
+ width: 100,
+ sorter: (a: CategoryType, b: CategoryType) => a.sort - b.sort,
+ },
+ {
+ title: '操作',
+ key: 'operation',
+ width: 250,
+ render: (_: any, record: CategoryType) => (
+
+
+
+
+
+ ),
+ },
+ ];
+
+ return (
+
+ );
+};
+
+export default FriendLinkCategory;
diff --git a/src/pages/friendLinkManage/friendLinkManage.less b/src/pages/friendLinkManage/friendLinkManage.less
new file mode 100644
index 0000000..b8528ad
--- /dev/null
+++ b/src/pages/friendLinkManage/friendLinkManage.less
@@ -0,0 +1,36 @@
+.friend-link-manage-container,
+.friend-link-category-container {
+ padding: 24px;
+ background-color: #fff;
+ min-height: calc(100vh - 184px);
+
+ .action-bar {
+ margin-bottom: 16px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .selected-count {
+ margin-left: 8px;
+ color: #999;
+ }
+ }
+
+ .ant-table-row-expand-icon {
+ margin-right: 8px;
+ }
+
+ .ant-card {
+ margin-bottom: 24px;
+ }
+
+ .ant-table-thead > tr > th {
+ background-color: #fafafa;
+ font-weight: 500;
+ }
+
+ .ant-modal-body {
+ max-height: 60vh;
+ overflow-y: auto;
+ }
+}
diff --git a/src/pages/friendLinkManage/friendLinkManage.tsx b/src/pages/friendLinkManage/friendLinkManage.tsx
new file mode 100644
index 0000000..06d7a36
--- /dev/null
+++ b/src/pages/friendLinkManage/friendLinkManage.tsx
@@ -0,0 +1,519 @@
+import React, { useState, useEffect } from 'react';
+import { useIntl } from 'umi';
+import { Card, Table, Button, Modal, Form, Input, Switch, Space, message, Upload, Tag, TreeSelect } from 'antd';
+import { PlusOutlined, DeleteOutlined, ExclamationCircleOutlined, UploadOutlined, SearchOutlined } from '@ant-design/icons';
+import type { UploadFile } from 'antd/es/upload/interface';
+import './friendLinkManage.less';
+
+// 友情链接类型定义
+interface FriendLinkType {
+ id: string;
+ name: string;
+ url: string;
+ logo: string;
+ sort: number;
+ status: string;
+ categoryId: string;
+ categoryName?: string;
+}
+
+// 友情链接分类类型定义
+interface CategoryType {
+ id: string;
+ name: string;
+ parentId: string | null;
+ level: number;
+ sort: number;
+ children?: CategoryType[];
+ key?: string;
+}
+
+const FriendLinkManage: React.FC = () => {
+ const intl = useIntl();
+ const [loading, setLoading] = useState(false);
+ const [modalVisible, setModalVisible] = useState(false);
+ const [isEdit, setIsEdit] = useState(false);
+ const [currentId, setCurrentId] = useState('');
+ const [linkData, setLinkData] = useState([]);
+ const [categoryData, setCategoryData] = useState([]);
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+ const [form] = Form.useForm();
+ const [fileList, setFileList] = useState([]);
+
+ // 模拟分类数据
+ const mockCategories: CategoryType[] = [
+ {
+ id: '1',
+ name: '政府机构',
+ parentId: null,
+ level: 1,
+ sort: 1,
+ children: [
+ {
+ id: '1-1',
+ name: '中央部委',
+ parentId: '1',
+ level: 2,
+ sort: 1,
+ },
+ {
+ id: '1-2',
+ name: '地方政府',
+ parentId: '1',
+ level: 2,
+ sort: 2,
+ children: [
+ {
+ id: '1-2-1',
+ name: '省级政府',
+ parentId: '1-2',
+ level: 3,
+ sort: 1,
+ },
+ {
+ id: '1-2-2',
+ name: '市级政府',
+ parentId: '1-2',
+ level: 3,
+ sort: 2,
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: '2',
+ name: '港口集团',
+ parentId: null,
+ level: 1,
+ sort: 2,
+ children: [
+ {
+ id: '2-1',
+ name: '沿海港口',
+ parentId: '2',
+ level: 2,
+ sort: 1,
+ },
+ {
+ id: '2-2',
+ name: '内河港口',
+ parentId: '2',
+ level: 2,
+ sort: 2,
+ }
+ ]
+ },
+ {
+ id: '3',
+ name: '航运企业',
+ parentId: null,
+ level: 1,
+ sort: 3,
+ }
+ ];
+
+ // 模拟数据
+ const mockData: FriendLinkType[] = [
+ {
+ id: '1',
+ name: '中远海运集团',
+ url: 'https://www.coscoshipping.com',
+ logo: 'https://www.coscoshipping.com/img/logo.png',
+ sort: 1,
+ status: '1', // 1-启用,0-禁用
+ categoryId: '3',
+ categoryName: '航运企业',
+ },
+ {
+ id: '2',
+ name: '上海港',
+ url: 'https://www.shanghaiport.com',
+ logo: 'https://www.shanghaiport.com/img/logo.png',
+ sort: 2,
+ status: '1',
+ categoryId: '2-1',
+ categoryName: '沿海港口',
+ },
+ {
+ id: '3',
+ name: '宁波港',
+ url: 'https://www.ningboport.com',
+ logo: 'https://www.ningboport.com/img/logo.png',
+ sort: 3,
+ status: '0',
+ categoryId: '2-1',
+ categoryName: '沿海港口',
+ },
+ ];
+
+ // 获取友情链接列表
+ const fetchLinkList = () => {
+ setLoading(true);
+ // 实际项目中应调用API
+ setTimeout(() => {
+ setLinkData(mockData);
+ setLoading(false);
+ }, 500);
+ };
+
+ // 获取分类列表
+ const fetchCategoryList = () => {
+ // 实际项目中应调用API
+ // 为每个节点添加key属性,用于TreeSelect组件
+ const processData = (data: CategoryType[]): CategoryType[] => {
+ return data.map(item => {
+ const newItem = { ...item, key: item.id };
+ if (item.children && item.children.length > 0) {
+ newItem.children = processData(item.children);
+ }
+ return newItem;
+ });
+ };
+
+ const processedData = processData(mockCategories);
+ setCategoryData(processedData);
+ };
+
+ // 获取分类名称
+ const getCategoryName = (categoryId: string): string => {
+ // 递归查找分类名称
+ const findCategoryName = (categories: CategoryType[], id: string): string => {
+ for (const category of categories) {
+ if (category.id === id) {
+ return category.name;
+ }
+ if (category.children && category.children.length > 0) {
+ const name = findCategoryName(category.children, id);
+ if (name) return name;
+ }
+ }
+ return '';
+ };
+
+ return findCategoryName(categoryData, categoryId);
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchCategoryList();
+ fetchLinkList();
+ }, []);
+
+ // 处理添加
+ const handleAdd = () => {
+ setIsEdit(false);
+ setCurrentId('');
+ form.resetFields();
+ setFileList([]);
+ setModalVisible(true);
+ };
+
+ // 处理编辑
+ const handleEdit = (record: FriendLinkType) => {
+ setIsEdit(true);
+ setCurrentId(record.id);
+ setModalVisible(true);
+
+ // 设置表单初始值
+ form.setFieldsValue({
+ name: record.name,
+ url: record.url,
+ sort: record.sort,
+ status: record.status === '1',
+ categoryId: record.categoryId,
+ });
+
+ // 设置上传文件列表
+ if (record.logo) {
+ setFileList([
+ {
+ uid: '-1',
+ name: 'logo.png',
+ status: 'done',
+ url: record.logo,
+ type: 'image/png',
+ size: 0,
+ } as UploadFile,
+ ]);
+ } else {
+ setFileList([]);
+ }
+ };
+
+ // 处理删除
+ const handleDelete = (id: string) => {
+ Modal.confirm({
+ title: intl.formatMessage({ id: 'friendLink.delete' }),
+ icon: ,
+ content: '确定要删除该友情链接吗?',
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ onOk() {
+ // 实际项目中应调用API
+ const newData = linkData.filter(item => item.id !== id);
+ setLinkData(newData);
+ message.success('删除成功');
+ },
+ });
+ };
+
+ // 处理批量删除
+ const handleBatchDelete = () => {
+ if (selectedRowKeys.length === 0) {
+ message.warning('请选择要删除的项');
+ return;
+ }
+
+ Modal.confirm({
+ title: intl.formatMessage({ id: 'friendLink.delete' }),
+ icon: ,
+ content: `确定要删除选中的 ${selectedRowKeys.length} 项吗?`,
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ onOk() {
+ // 实际项目中应调用API
+ const newData = linkData.filter(item => !selectedRowKeys.includes(item.id));
+ setLinkData(newData);
+ setSelectedRowKeys([]);
+ message.success('删除成功');
+ },
+ });
+ };
+
+ // 处理表单提交
+ const handleModalSubmit = () => {
+ form.validateFields().then(values => {
+ // 准备提交数据
+ const formData = {
+ ...values,
+ status: values.status ? '1' : '0',
+ logo: fileList.length > 0 ? fileList[0].url || fileList[0].thumbUrl : '',
+ categoryName: getCategoryName(values.categoryId),
+ };
+
+ if (isEdit) {
+ // 编辑模式
+ const newData = linkData.map(item =>
+ item.id === currentId ? { ...item, ...formData } : item
+ );
+ setLinkData(newData);
+ message.success('更新成功');
+ } else {
+ // 新增模式
+ const newId = (Math.max(...linkData.map(item => parseInt(item.id))) + 1).toString();
+ const newItem = {
+ id: newId,
+ ...formData,
+ };
+ setLinkData([...linkData, newItem]);
+ message.success('添加成功');
+ }
+
+ setModalVisible(false);
+ form.resetFields();
+ setFileList([]);
+ });
+ };
+
+ // 处理文件上传变化
+ const handleFileChange = (info: { fileList: UploadFile[] }) => {
+ setFileList(info.fileList);
+ };
+
+ // 表格列定义
+ const columns = [
+ {
+ title: intl.formatMessage({ id: 'friendLink.name' }),
+ dataIndex: 'name',
+ key: 'name',
+ },
+ {
+ title: '所属分类',
+ dataIndex: 'categoryName',
+ key: 'categoryName',
+ },
+ {
+ title: intl.formatMessage({ id: 'friendLink.url' }),
+ dataIndex: 'url',
+ key: 'url',
+ render: (text: string) => (
+
+ {text}
+
+ ),
+ },
+ {
+ title: intl.formatMessage({ id: 'friendLink.logo' }),
+ dataIndex: 'logo',
+ key: 'logo',
+ render: (text: string) => (
+ text ?
: '-'
+ ),
+ },
+ {
+ title: intl.formatMessage({ id: 'friendLink.sort' }),
+ dataIndex: 'sort',
+ key: 'sort',
+ sorter: (a: FriendLinkType, b: FriendLinkType) => a.sort - b.sort,
+ },
+ {
+ title: intl.formatMessage({ id: 'friendLink.status' }),
+ dataIndex: 'status',
+ key: 'status',
+ render: (text: string) => (
+ text === '1'
+ ? {intl.formatMessage({ id: 'friendLink.enabled' })}
+ : {intl.formatMessage({ id: 'friendLink.disabled' })}
+ ),
+ },
+ {
+ title: intl.formatMessage({ id: 'friendLink.operation' }),
+ key: 'operation',
+ render: (_: any, record: FriendLinkType) => (
+
+
+
+
+ ),
+ },
+ ];
+
+ // 行选择配置
+ const rowSelection = {
+ selectedRowKeys,
+ onChange: (keys: React.Key[]) => setSelectedRowKeys(keys),
+ };
+
+ return (
+
+ );
+};
+
+export default FriendLinkManage;
diff --git a/src/pages/helpManage/helpManage.less b/src/pages/helpManage/helpManage.less
index 40af3da..40a8938 100644
--- a/src/pages/helpManage/helpManage.less
+++ b/src/pages/helpManage/helpManage.less
@@ -8,4 +8,56 @@
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}
+
+ .search-area {
+ display: flex;
+ margin-bottom: 16px;
+ align-items: center;
+ }
+}
+
+.common-container {
+ background: #fff;
+ border-radius: 4px;
+ padding: 24px;
+ min-height: calc(100vh - 144px);
+
+ .filter-action-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 16px;
+
+ .filter-form {
+ flex: 1;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+
+ .filter-btns {
+ display: flex;
+ gap: 8px;
+ }
+
+ .right-buttons {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .selected-count {
+ color: #ff4d4f;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .content-area {
+ margin-top: 16px;
+ }
+
+ :global {
+ .ant-form-item {
+ margin-bottom: 16px;
+ }
+ }
}
diff --git a/src/pages/helpManage/helpManage.tsx b/src/pages/helpManage/helpManage.tsx
index 32893a9..0d1a095 100644
--- a/src/pages/helpManage/helpManage.tsx
+++ b/src/pages/helpManage/helpManage.tsx
@@ -1,16 +1,629 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import { useIntl } from 'umi';
+import { Button, Table, Modal, message, Input, Select, Form, Tooltip, Switch, Tag, Tabs } from 'antd';
+import {
+ PlusOutlined,
+ DeleteOutlined,
+ ExclamationCircleOutlined,
+ SearchOutlined,
+} from '@ant-design/icons';
import './helpManage.less';
+// 引入封装的WangEditor组件
+import WangEditor from '@/components/WangEidtor/WangEidtor';
+// 引入API
+import { getHelpList, addHelp, updateHelp, deleteHelp, batchDeleteHelp, updateHelpStatus, updateHelpTopStatus } from '@/servers/api/help';
+
+const { Option } = Select;
+const { TabPane } = Tabs;
+const { confirm } = Modal;
+
+// 帮助中心条目类型定义
+interface HelpItemType extends API.HelpRecord {
+ key: string;
+}
const HelpManage: React.FC = () => {
const intl = useIntl();
+ const [loading, setLoading] = useState(false);
+ const [helpData, setHelpData] = useState([]);
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+ const [form] = Form.useForm();
+ const [modalVisible, setModalVisible] = useState(false);
+ const [isEdit, setIsEdit] = useState(false);
+ const [currentId, setCurrentId] = useState('');
+ const [modalForm] = Form.useForm();
+ const [activeTabKey, setActiveTabKey] = useState('zh');
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total: number) => `共 ${total} 条记录`,
+ });
+ const [searchParams, setSearchParams] = useState({});
+
+ // 获取帮助中心列表数据
+ const fetchHelpList = (current: number = 1, pageSize: number = 10) => {
+ setLoading(true);
+ // 调用API
+ getHelpList({
+ ...searchParams,
+ pageNo: current,
+ pageSize,
+ })
+ .then(res => {
+ if (res && res.success && res.data && res.data.records) {
+ const records = res.data.records.map((item: API.HelpRecord) => ({
+ ...item,
+ key: item.id,
+ }));
+ setHelpData(records);
+ setPagination({
+ ...pagination,
+ current,
+ pageSize,
+ total: res.data.total || records.length,
+ });
+ } else {
+ message.error('获取帮助中心列表失败');
+ }
+ })
+ .catch(error => {
+ console.error('获取帮助中心列表失败:', error);
+ message.error('获取帮助中心列表失败');
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchHelpList();
+ }, []);
+
+ // 处理搜索
+ const handleSearch = (values: any) => {
+ setSearchParams(values);
+ fetchHelpList(1, pagination.pageSize);
+ };
+
+ // 处理添加
+ const handleAdd = () => {
+ setIsEdit(false);
+ setCurrentId('');
+ modalForm.resetFields();
+ setModalVisible(true);
+ setActiveTabKey('zh');
+ };
+
+ // 处理编辑
+ const handleEdit = (record: HelpItemType) => {
+ // 检查是否为已发布状态
+ if (record.status === '1') {
+ message.warning('已发布的帮助不能编辑');
+ return;
+ }
+
+ setIsEdit(true);
+ setCurrentId(record.id);
+ setModalVisible(true);
+ setActiveTabKey('zh');
+
+ // 填充表单数据
+ modalForm.setFieldsValue({
+ isTop: record.isTop === '1',
+ type: record.type,
+ titleZh: record.title,
+ titleEn: record.titleEn || '',
+ contentZh: record.content,
+ contentEn: record.contentEn || '',
+ });
+ };
+
+ // 处理删除
+ const showDeleteConfirm = (record: HelpItemType) => {
+ // 检查是否为已发布状态
+ if (record.status === '1') {
+ message.warning('已发布的帮助不能删除');
+ return;
+ }
+
+ confirm({
+ title: intl.formatMessage({ id: 'helpCenter.confirmDelete' }),
+ icon: ,
+ content: `标题: ${record.title}`,
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ maskClosable: false,
+ onOk: async () => {
+ try {
+ // 调用删除API
+ await deleteHelp(record.id);
+ // 更新本地数据
+ const newData = helpData.filter(item => item.id !== record.id);
+ setHelpData(newData);
+ message.success('删除成功');
+ } catch (error) {
+ console.error('删除帮助失败:', error);
+ message.error('删除失败');
+ }
+ },
+ });
+ };
+
+ // 处理发布/下架
+ const handlePublishStatus = async (record: HelpItemType) => {
+ // 状态: 0-草稿,1-已发布
+ const isPublished = record.status === '1';
+ const actionText = isPublished ? '下架' : '发布';
+ const newStatus = isPublished ? '0' : '1';
+
+ try {
+ // 调用API更新状态
+ await updateHelpStatus(record.id, newStatus);
+ // 更新本地数据
+ const newData = helpData.map(item =>
+ item.id === record.id ? { ...item, status: newStatus } : item
+ );
+ setHelpData(newData);
+ message.success(`${actionText}成功`);
+ } catch (error) {
+ console.error(`${actionText}失败:`, error);
+ message.error(`${actionText}失败`);
+ }
+ };
+
+ // 处理置顶/取消置顶
+ const handleToggleTop = async (record: HelpItemType) => {
+ const isTop = record.isTop === '1';
+ const actionText = isTop ? '取消置顶' : '置顶';
+ const newIsTop = isTop ? '0' : '1';
+
+ try {
+ // 调用API更新置顶状态
+ await updateHelpTopStatus(record.id, newIsTop);
+ // 更新本地数据
+ const newData = helpData.map(item =>
+ item.id === record.id ? { ...item, isTop: newIsTop } : item
+ );
+ setHelpData(newData);
+ message.success(`${actionText}成功`);
+ } catch (error) {
+ console.error(`${actionText}失败:`, error);
+ message.error(`${actionText}失败`);
+ }
+ };
+
+ // 获取状态标签
+ const getStatusTag = (status: string) => {
+ switch (status) {
+ case '0':
+ return 草稿;
+ case '1':
+ return 已发布;
+ case '2':
+ return 已下架;
+ default:
+ return 未知;
+ }
+ };
+
+ // 处理表格分页变化
+ const handleTableChange = (newPagination: any) => {
+ fetchHelpList(newPagination.current, newPagination.pageSize);
+ };
+
+ // 处理批量删除
+ const handleBatchDelete = () => {
+ confirm({
+ title: intl.formatMessage({ id: 'helpCenter.batchDelete' }),
+ icon: ,
+ content: '删除后无法恢复',
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ maskClosable: false,
+ onOk: async () => {
+ setLoading(true);
+ try {
+ // 获取可删除的ID(非已发布状态)
+ const deleteIds = selectedRowKeys.filter(key => {
+ const record = helpData.find(item => item.key === key);
+ return record && record.status !== '1';
+ }) as string[];
+
+ if (deleteIds.length > 0) {
+ // 调用批量删除API
+ await batchDeleteHelp(deleteIds);
+ // 更新本地数据
+ const newData = helpData.filter(item => !deleteIds.includes(item.key));
+ setHelpData(newData);
+ setSelectedRowKeys([]);
+ message.success('删除成功');
+ } else {
+ message.warning('没有可删除的帮助');
+ }
+ } catch (error) {
+ console.error('批量删除失败:', error);
+ message.error('批量删除失败');
+ } finally {
+ setLoading(false);
+ }
+ },
+ });
+ };
+
+ // 行选择限制
+ const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+ setSelectedRowKeys(newSelectedRowKeys);
+ };
+
+ const rowSelection = {
+ selectedRowKeys,
+ onChange: onSelectChange,
+ getCheckboxProps: (record: HelpItemType) => ({
+ disabled: record.status === '1', // 已发布的不能选择
+ }),
+ };
+
+ const hasSelected = selectedRowKeys.length > 0;
+
+ // 处理模态框提交
+ const handleModalSubmit = () => {
+ // 先触发所有字段的验证
+ modalForm.validateFields()
+ .then(async values => {
+ try {
+ const helpParams: API.HelpRequest = {
+ title: values.titleZh,
+ titleEn: values.titleEn || '',
+ content: values.contentZh,
+ contentEn: values.contentEn || '',
+ type: values.type,
+ isTop: values.isTop ? '1' : '0',
+ settingEn: values.titleEn && values.contentEn ? 1 : 0,
+ };
+
+ if (isEdit) {
+ // 编辑模式,调用更新API
+ await updateHelp(currentId, helpParams);
+ message.success('更新成功');
+ } else {
+ // 新增模式,调用添加API
+ await addHelp(helpParams);
+ message.success('添加成功');
+ }
+
+ setModalVisible(false);
+ modalForm.resetFields();
+ // 刷新列表
+ fetchHelpList(pagination.current, pagination.pageSize);
+ } catch (error) {
+ console.error(isEdit ? '更新帮助失败:' : '添加帮助失败:', error);
+ message.error(isEdit ? '更新失败' : '添加失败');
+ }
+ })
+ .catch(errorInfo => {
+ // 获取所有字段的错误信息
+ const errorFields = errorInfo.errorFields || [];
+
+ // 检查是否有中文标题或内容的错误
+ const hasZhError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('titleZh') || fieldName.includes('contentZh');
+ });
+
+ // 检查是否有英文标题或内容的错误
+ const hasEnError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('titleEn') || fieldName.includes('contentEn');
+ });
+
+ // 如果有中文字段错误,切换到中文Tab
+ if (hasZhError) {
+ setActiveTabKey('zh');
+ }
+ // 如果只有英文字段错误,切换到英文Tab
+ else if (hasEnError && !hasZhError) {
+ setActiveTabKey('en');
+ }
+
+ console.log('表单验证失败:', errorInfo);
+ });
+ };
+
+ // 处理Tab切换
+ const handleTabChange = (key: string) => {
+ setActiveTabKey(key);
+ };
+
+ // 处理模态框取消
+ const handleModalCancel = () => {
+ setModalVisible(false);
+ modalForm.resetFields();
+ };
+
+ // 表格列定义
+ const columns = [
+ {
+ title: '序号',
+ dataIndex: 'id',
+ key: 'id',
+ align: 'center' as const,
+ width: 80,
+ },
+ {
+ title: '标题',
+ dataIndex: 'title',
+ key: 'title',
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (title: string, record: HelpItemType) => (
+
+
+ {record.isTop === '1' && 置顶}
+ {title}
+
+
+ ),
+ },
+ {
+ title: '分类',
+ dataIndex: 'type',
+ key: 'type',
+ align: 'center' as const,
+ },
+ {
+ title: '日期',
+ dataIndex: 'createTime',
+ key: 'createTime',
+ align: 'center' as const,
+ sorter: (a: HelpItemType, b: HelpItemType) =>
+ new Date(a.createTime).getTime() - new Date(b.createTime).getTime(),
+ },
+ {
+ title: '状态',
+ dataIndex: 'status',
+ key: 'status',
+ align: 'center' as const,
+ render: (status: string) => getStatusTag(status),
+ },
+ {
+ title: '发布人',
+ dataIndex: 'createBy',
+ key: 'createBy',
+ align: 'center' as const,
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 300,
+ align: 'center' as const,
+ render: (_: unknown, record: HelpItemType) => (
+ <>
+ {record.status === '1' ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+
+
+ >
+ )}
+ >
+ ),
+ },
+ ];
+
+ // 分类选项
+ const categoryOptions = [
+ { value: '注册指南', label: '注册指南' },
+ { value: '投标指南', label: '投标指南' },
+ { value: '常见问题', label: '常见问题' },
+ { value: '联系我们', label: '联系我们' },
+ ];
return (
-
-
{intl.formatMessage({ id: 'menu.帮助中心管理' })}
-
- {/* 帮助中心管理内容 */}
+
+
+
+
+
+
+
+
+
+
+
+
+ }>
+ 搜索
+
+ }
+ onClick={() => {
+ form.resetFields();
+ setSearchParams({});
+ handleSearch({});
+ }}
+ >
+ 重置
+
+
+
+
+
+ }
+ onClick={handleAdd}
+ >
+ 新增
+
+ }
+ onClick={handleBatchDelete}
+ disabled={!hasSelected}
+ loading={loading}
+ >
+ 删除
+
+ {hasSelected && (
+
+ 已选择 {selectedRowKeys.length} 项
+
+ )}
+
+
+
+
+ {/* 新增/编辑模态框 */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
};
diff --git a/src/pages/noticeManage/noticeManage.less b/src/pages/noticeManage/noticeManage.less
index e003f20..e69de29 100644
--- a/src/pages/noticeManage/noticeManage.less
+++ b/src/pages/noticeManage/noticeManage.less
@@ -1,11 +0,0 @@
-.notice-manage-container {
- padding: 24px;
-
- .notice-manage-content {
- background: #fff;
- padding: 24px;
- min-height: 500px;
- border-radius: 4px;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
- }
-}
diff --git a/src/pages/noticeManage/noticeManage.tsx b/src/pages/noticeManage/noticeManage.tsx
index f7cd3c4..bf76580 100644
--- a/src/pages/noticeManage/noticeManage.tsx
+++ b/src/pages/noticeManage/noticeManage.tsx
@@ -1,16 +1,664 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import { useIntl } from 'umi';
+import { Button, Table, Modal, message, Input, Select, Form, Tooltip, Switch, Tag, Tabs } from 'antd';
+import {
+ PlusOutlined,
+ DeleteOutlined,
+ ExclamationCircleOutlined,
+ SearchOutlined,
+ VerticalAlignTopOutlined,
+} from '@ant-design/icons';
+import { getNoticeList, addNotice, updateNotice, deleteNotice, batchDeleteNotice, updateNoticeStatus, updateNoticeTopStatus } from '@/servers/api/notice';
import './noticeManage.less';
+// 引入封装的WangEditor组件
+import WangEditor from '@/components/WangEidtor/WangEidtor';
+
+const { Option } = Select;
+const { TextArea } = Input;
+const { TabPane } = Tabs;
+
+type NoticeType = API.NoticeRecord & {
+ key: string;
+};
const NoticeManage: React.FC = () => {
const intl = useIntl();
+ const [selectedRowKeys, setSelectedRowKeys] = useState
([]);
+ const [loading, setLoading] = useState(false);
+ const [form] = Form.useForm();
+ const [modalVisible, setModalVisible] = useState(false);
+ const [isEdit, setIsEdit] = useState(false);
+ const [currentId, setCurrentId] = useState('');
+ const [modalForm] = Form.useForm();
+
+ // 富文本内容状态
+ const [htmlZh, setHtmlZh] = useState('');
+ const [htmlEn, setHtmlEn] = useState('');
+
+ const [noticeData, setNoticeData] = useState([
+ {
+ key: '1',
+ id: '1',
+ title: '系统维护通知',
+ titleEn: 'System Maintenance Notice',
+ content: '系统将于2023年7月1日进行例行维护,请提前做好准备。',
+ contentEn: 'The system will undergo routine maintenance on July 1, 2023. Please prepare in advance.',
+ createTime: '2023-06-25 10:30:00',
+ createBy: 'admin',
+ status: '1', // 已发布
+ isTop: '1', // 已置顶
+ settingEn: 1, // 设置英文
+ },
+ {
+ key: '2',
+ id: '2',
+ title: '新功能上线通知',
+ titleEn: 'New Feature Launch',
+ content: '系统新增了XXXX功能,欢迎使用。',
+ contentEn: 'The system has added XXXX function, welcome to use.',
+ createTime: '2023-06-20 14:45:00',
+ createBy: 'admin',
+ status: '0', // 草稿
+ isTop: '0', // 未置顶
+ settingEn: 1,
+ },
+ {
+ key: '3',
+ id: '3',
+ title: '用户反馈调查',
+ titleEn: '',
+ content: '为了提升用户体验,我们正在收集用户反馈...',
+ contentEn: '',
+ createTime: '2023-06-18 09:15:00',
+ createBy: 'admin',
+ status: '1', // 已发布
+ isTop: '0', // 未置顶
+ settingEn: 0,
+ },
+ ]);
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 3,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total: number) => `共 ${total} 条记录`,
+ });
+ const [searchParams, setSearchParams] = useState({});
+ const [activeTabKey, setActiveTabKey] = useState('zh');
+
+ // 获取通知列表(模拟数据)
+ const fetchNoticeList = (current: number = 1, pageSize: number = 10) => {
+ setLoading(true);
+ // 实际项目中应调用API
+ getNoticeList({
+ ...searchParams,
+ })
+ .then(res => {
+ // 此处为模拟数据,实际项目中应使用API返回的数据
+ setNoticeData([
+ {
+ key: '1',
+ id: '1',
+ title: '系统维护通知',
+ titleEn: 'System Maintenance Notice',
+ content: '系统将于2023年7月1日进行例行维护,请提前做好准备。',
+ contentEn: 'The system will undergo routine maintenance on July 1, 2023. Please prepare in advance.',
+ createTime: '2023-06-25 10:30:00',
+ createBy: 'admin',
+ status: '1', // 已发布
+ isTop: '1', // 已置顶
+ settingEn: 1,
+ },
+ {
+ key: '2',
+ id: '2',
+ title: '新功能上线通知',
+ titleEn: 'New Feature Launch',
+ content: '系统新增了XXXX功能,欢迎使用。',
+ contentEn: 'The system has added XXXX function, welcome to use.',
+ createTime: '2023-06-20 14:45:00',
+ createBy: 'admin',
+ status: '0', // 草稿
+ isTop: '0', // 未置顶
+ settingEn: 1,
+ },
+ {
+ key: '3',
+ id: '3',
+ title: '用户反馈调查',
+ titleEn: '',
+ content: '为了提升用户体验,我们正在收集用户反馈...',
+ contentEn: '',
+ createTime: '2023-06-18 09:15:00',
+ createBy: 'admin',
+ status: '1', // 已发布
+ isTop: '0', // 未置顶
+ settingEn: 0,
+ },
+ ]);
+ setPagination({
+ ...pagination,
+ current,
+ pageSize,
+ total: 3, // 应该是res.total
+ });
+ })
+ .catch(error => {
+ console.error('获取通知列表失败:', error);
+ message.error('获取通知列表失败');
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchNoticeList();
+ }, []);
+
+ // 处理编辑
+ const handleEdit = (record: NoticeType) => {
+ // 检查是否为已发布状态
+ if (record.status === '1') {
+ message.warning('已发布的通知不能编辑');
+ return;
+ }
+
+ setIsEdit(true);
+ setCurrentId(record.id);
+ setModalVisible(true);
+ setActiveTabKey('zh');
+
+ // 填充表单数据
+ modalForm.setFieldsValue({
+ isTop: record.isTop === '1',
+ titleZh: record.title,
+ titleEn: record.titleEn,
+ contentZh: record.content,
+ contentEn: record.contentEn || '',
+ });
+ };
+
+ // 处理删除
+ const showDeleteConfirm = (record: NoticeType) => {
+ // 检查是否为已发布状态
+ if (record.status === '1') {
+ message.warning('已发布的通知不能删除');
+ return;
+ }
+
+ Modal.confirm({
+ title: '确定要删除该通知吗?',
+ icon: ,
+ content: `标题: ${record.title}`,
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ maskClosable: false,
+ onOk: async () => {
+ try {
+ // 调用删除API
+ await deleteNotice(record.id);
+ // 更新本地数据
+ const newData = noticeData.filter(item => item.id !== record.id);
+ setNoticeData(newData);
+ message.success('删除成功');
+ } catch (error) {
+ console.error('删除通知失败:', error);
+ message.error('删除失败');
+ }
+ },
+ });
+ };
+
+ // 处理发布/下架
+ const handlePublishStatus = async (record: NoticeType) => {
+ // 状态: 0-草稿,1-已发布
+ const isPublished = record.status === '1';
+ const actionText = isPublished ? '下架' : '发布';
+ const newStatus = isPublished ? '0' : '1';
+
+ try {
+ // 调用API更新状态
+ await updateNoticeStatus(record.id, newStatus);
+ // 更新本地数据
+ const newData = noticeData.map(item =>
+ item.id === record.id ? { ...item, status: newStatus } : item
+ );
+ setNoticeData(newData);
+ message.success(`${actionText}成功`);
+ } catch (error) {
+ console.error(`${actionText}失败:`, error);
+ message.error(`${actionText}失败`);
+ }
+ };
+
+ // 处理置顶/取消置顶
+ const handleToggleTop = async (record: NoticeType) => {
+ const isTop = record.isTop === '1';
+ const actionText = isTop ? '取消置顶' : '置顶';
+ const newIsTop = isTop ? '0' : '1';
+
+ try {
+ // 调用API更新置顶状态
+ await updateNoticeTopStatus(record.id, newIsTop);
+ // 更新本地数据
+ const newData = noticeData.map(item =>
+ item.id === record.id ? { ...item, isTop: newIsTop } : item
+ );
+ setNoticeData(newData);
+ message.success(`${actionText}成功`);
+ } catch (error) {
+ console.error(`${actionText}失败:`, error);
+ message.error(`${actionText}失败`);
+ }
+ };
+
+ // 获取状态标签
+ const getStatusTag = (status: string) => {
+ switch (status) {
+ case '0':
+ return 草稿;
+ case '1':
+ return 已发布;
+ case '2':
+ return 已下架;
+ default:
+ return 未知;
+ }
+ };
+
+ // 处理表格分页变化
+ const handleTableChange = (newPagination: any) => {
+ fetchNoticeList(newPagination.current, newPagination.pageSize);
+ };
+
+ const columns = [
+ {
+ title: '序号',
+ dataIndex: 'id',
+ key: 'id',
+ align: 'center' as const,
+ width: 80,
+ },
+ {
+ title: '标题',
+ dataIndex: 'title',
+ key: 'title',
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (title: string, record: NoticeType) => (
+
+
+ {record.isTop === '1' && 置顶}
+ {title}
+
+
+ ),
+ },
+ {
+ title: '日期',
+ dataIndex: 'createTime',
+ key: 'createTime',
+ align: 'center' as const,
+ },
+ {
+ title: '状态',
+ dataIndex: 'status',
+ key: 'status',
+ align: 'center' as const,
+ render: (status: string) => getStatusTag(status),
+ },
+ {
+ title: '发布人',
+ dataIndex: 'createBy',
+ key: 'createBy',
+ align: 'center' as const,
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 300,
+ align: 'center' as const,
+ render: (_: unknown, record: NoticeType) => (
+ <>
+ {record.status === '1' ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+
+
+
+ >
+ )}
+ >
+ ),
+ },
+ ];
+
+ // 行选择限制
+ const checkSelectable = (record: NoticeType) => {
+ return record.status !== '1'; // 已发布的不能选择
+ };
+
+ const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+ setSelectedRowKeys(newSelectedRowKeys);
+ };
+
+ const rowSelection = {
+ selectedRowKeys,
+ onChange: onSelectChange,
+ getCheckboxProps: (record: NoticeType) => ({
+ disabled: record.status === '1', // 已发布的不能选择
+ }),
+ };
+
+ const hasSelected = selectedRowKeys.length > 0;
+
+ // 处理添加
+ const handleAdd = () => {
+ setIsEdit(false);
+ setCurrentId('');
+ modalForm.resetFields();
+ setModalVisible(true);
+ setActiveTabKey('zh');
+ };
+
+ // 处理批量删除
+ const handleBatchDelete = () => {
+ Modal.confirm({
+ title: '确定要删除选中的通知吗?',
+ icon: ,
+ content: '删除后无法恢复',
+ okText: '确定',
+ okType: 'danger',
+ cancelText: '取消',
+ maskClosable: false,
+ onOk: async () => {
+ setLoading(true);
+ try {
+ // 获取可删除的ID(非已发布状态)
+ const deleteIds = selectedRowKeys.filter(key => {
+ const record = noticeData.find(item => item.key === key);
+ return record && record.status !== '1';
+ }) as string[];
+
+ if (deleteIds.length > 0) {
+ // 调用批量删除API
+ await batchDeleteNotice(deleteIds);
+ // 更新本地数据
+ const newData = noticeData.filter(item => !deleteIds.includes(item.key));
+ setNoticeData(newData);
+ setSelectedRowKeys([]);
+ message.success('删除成功');
+ } else {
+ message.warning('没有可删除的通知');
+ }
+ } catch (error) {
+ console.error('批量删除失败:', error);
+ message.error('批量删除失败');
+ } finally {
+ setLoading(false);
+ }
+ },
+ });
+ };
+
+ // 处理搜索
+ const handleSearch = (values: API.NoticeSearchParams) => {
+ setSearchParams(values);
+ fetchNoticeList(1, pagination.pageSize);
+ };
+
+ // 处理模态框提交
+ const handleModalSubmit = () => {
+ // 先触发所有字段的验证
+ modalForm.validateFields()
+ .then(async values => {
+ try {
+ const noticeParams: API.NoticeRequest = {
+ title: values.titleZh,
+ titleEn: values.titleEn || '',
+ content: values.contentZh,
+ contentEn: values.contentEn || '',
+ isTop: values.isTop ? '1' : '0',
+ settingEn: values.titleEn && values.contentEn ? 1 : 0,
+ };
+
+ if (isEdit) {
+ // 编辑模式,调用更新API
+ await updateNotice(currentId, noticeParams);
+ message.success('更新成功');
+ } else {
+ // 新增模式,调用添加API
+ await addNotice(noticeParams);
+ message.success('添加成功');
+ }
+
+ setModalVisible(false);
+ modalForm.resetFields();
+ // 刷新列表
+ fetchNoticeList(pagination.current, pagination.pageSize);
+ } catch (error) {
+ console.error(isEdit ? '更新通知失败:' : '添加通知失败:', error);
+ message.error(isEdit ? '更新失败' : '添加失败');
+ }
+ })
+ .catch(errorInfo => {
+ // 获取所有字段的错误信息
+ const errorFields = errorInfo.errorFields || [];
+
+ // 检查是否有中文标题或内容的错误
+ const hasZhError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('titleZh') || fieldName.includes('contentZh');
+ });
+
+ // 检查是否有英文标题或内容的错误
+ const hasEnError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('titleEn') || fieldName.includes('contentEn');
+ });
+
+ // 如果有中文字段错误,切换到中文Tab
+ if (hasZhError) {
+ setActiveTabKey('zh');
+ }
+ // 如果只有英文字段错误,切换到英文Tab
+ else if (hasEnError && !hasZhError) {
+ setActiveTabKey('en');
+ }
+
+ console.log('表单验证失败:', errorInfo);
+ });
+ };
+
+ // 处理Tab切换
+ const handleTabChange = (key: string) => {
+ setActiveTabKey(key);
+ };
+
+ // 处理模态框取消
+ const handleModalCancel = () => {
+ setModalVisible(false);
+ modalForm.resetFields();
+ };
return (
-
-
{intl.formatMessage({ id: 'menu.通知中心管理' })}
-
- {/* 通知中心管理内容 */}
+
);
};
diff --git a/src/pages/policyManage/policyManage.tsx b/src/pages/policyManage/policyManage.tsx
index c042ab1..035ed53 100644
--- a/src/pages/policyManage/policyManage.tsx
+++ b/src/pages/policyManage/policyManage.tsx
@@ -8,6 +8,7 @@ import {
SearchOutlined,
} from '@ant-design/icons';
import { addPolicy, getPolicyPage, deletePolicy, publishPolicy, unpublishPolicy, getPolicyDetail, updatePolicy } from '@/servers/api/policy';
+import WangEditor from '@/components/WangEidtor/WangEidtor';
const { Option } = Select;
const { TabPane } = Tabs;
@@ -46,6 +47,7 @@ const PolicyManage: React.FC = () => {
showTotal: (total) => `共 ${total} 条记录`,
});
const [searchParams, setSearchParams] = useState
({});
+ const [activeTabKey, setActiveTabKey] = useState('zh');
// 获取政策列表
const fetchPolicyList = async (current: number = 1, pageSize: number = 10) => {
@@ -103,6 +105,7 @@ const PolicyManage: React.FC = () => {
setIsEdit(true);
setCurrentId(record.id);
setModalVisible(true);
+ setActiveTabKey('zh');
try {
const response = await getPolicyDetail(record.id);
@@ -287,6 +290,7 @@ const PolicyManage: React.FC = () => {
setCurrentId('');
modalForm.resetFields();
setModalVisible(true);
+ setActiveTabKey('zh');
};
// 处理批量删除
@@ -332,42 +336,75 @@ const PolicyManage: React.FC = () => {
fetchPolicyList(1, pagination.pageSize as number); // 搜索时重置为第一页
};
+ // 处理Tab切换
+ const handleTabChange = (key: string) => {
+ setActiveTabKey(key);
+ };
+
// 处理模态框提交
const handleModalSubmit = () => {
- modalForm.validateFields().then(async values => {
- try {
- // 转换表单数据为API需要的格式
- const requestData: API.PolicyRequest = {
- title: values.titleZh,
- titleEn: values.titleEn || '',
- content: values.contentZh,
- contentEn: values.contentEn || '',
- isTop: values.isTop ? '1' : '0',
- settingEn: values.titleEn && values.contentEn ? 1 : 0,
- };
+ modalForm.validateFields()
+ .then(async values => {
+ try {
+ // 转换表单数据为API需要的格式
+ const requestData: API.PolicyRequest = {
+ title: values.titleZh,
+ titleEn: values.titleEn || '',
+ content: values.contentZh,
+ contentEn: values.contentEn || '',
+ isTop: values.isTop ? '1' : '0',
+ settingEn: values.titleEn && values.contentEn ? 1 : 0,
+ };
- let response;
- if (isEdit) {
- response = await updatePolicy(currentId, requestData);
- } else {
- response = await addPolicy(requestData);
+ let response;
+ if (isEdit) {
+ response = await updatePolicy(currentId, requestData);
+ } else {
+ response = await addPolicy(requestData);
+ }
+
+ if (response && response.success) {
+ message.success(isEdit ? '更新成功' : '添加成功');
+ setModalVisible(false);
+ modalForm.resetFields();
+ fetchPolicyList(1, pagination.pageSize as number); // 操作成功后返回第一页
+ } else {
+ message.error(response.message || (isEdit ? '更新失败' : '添加失败'));
+ }
+ } catch (error) {
+ console.error(isEdit ? '更新政策失败:' : '添加政策失败:', error);
+ message.error(isEdit ? '更新失败' : '添加失败');
+ }
+ })
+ .catch(errorInfo => {
+ // 获取所有字段的错误信息
+ const errorFields = errorInfo.errorFields || [];
+
+ // 检查是否有中文标题或内容的错误
+ const hasZhError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('titleZh') || fieldName.includes('contentZh');
+ });
+
+ // 检查是否有英文标题或内容的错误
+ const hasEnError = errorFields.some((field: any) => {
+ if (!field.name) return false;
+ const fieldName = Array.isArray(field.name) ? field.name.join('.') : String(field.name);
+ return fieldName.includes('titleEn') || fieldName.includes('contentEn');
+ });
+
+ // 如果有中文字段错误,切换到中文Tab
+ if (hasZhError) {
+ setActiveTabKey('zh');
+ }
+ // 如果只有英文字段错误,切换到英文Tab
+ else if (hasEnError && !hasZhError) {
+ setActiveTabKey('en');
}
- if (response && response.success) {
- message.success(isEdit ? '更新成功' : '添加成功');
- setModalVisible(false);
- modalForm.resetFields();
- fetchPolicyList(1, pagination.pageSize as number); // 操作成功后返回第一页
- } else {
- message.error(response.message || (isEdit ? '更新失败' : '添加失败'));
- }
- } catch (error) {
- console.error(isEdit ? '更新政策失败:' : '添加政策失败:', error);
- message.error(isEdit ? '更新失败' : '添加失败');
- }
- }).catch(info => {
- console.log('校验失败:', info);
- });
+ console.log('表单验证失败:', errorInfo);
+ });
};
// 处理模态框取消
@@ -458,7 +495,7 @@ const PolicyManage: React.FC = () => {
visible={modalVisible}
onOk={handleModalSubmit}
onCancel={handleModalCancel}
- width={800}
+ width={900}
maskClosable={false}
destroyOnClose
>
@@ -472,7 +509,7 @@ const PolicyManage: React.FC = () => {
-
+
{
label="正文内容(中文)"
rules={[{ required: true, message: '请输入中文正文' }]}
>
-
- {/* 实际项目中应替换为富文本编辑器组件 */}
@@ -507,14 +543,13 @@ const PolicyManage: React.FC = () => {
-
- {/* 实际项目中应替换为富文本编辑器组件 */}
diff --git a/src/pages/userQuestionManage/readQuestionManage.tsx b/src/pages/userQuestionManage/readQuestionManage.tsx
new file mode 100644
index 0000000..3eefdb6
--- /dev/null
+++ b/src/pages/userQuestionManage/readQuestionManage.tsx
@@ -0,0 +1,548 @@
+import React, { useState, useEffect } from 'react';
+import { useIntl } from 'umi';
+import { Button, Table, Modal, message, Input, Form, Tooltip, Tag, Space, Switch } from 'antd';
+import {
+ DeleteOutlined,
+ ExclamationCircleOutlined,
+ SearchOutlined,
+ EyeOutlined
+} from '@ant-design/icons';
+import '../userQuestionManage/userQuestionManage.less';
+// 引入封装的WangEditor组件
+import WangEditor from '@/components/WangEidtor/WangEidtor';
+
+const { TextArea } = Input;
+const { confirm } = Modal;
+
+// 用户提问类型定义
+interface QuestionItemType {
+ key: string;
+ id: string;
+ title: string;
+ category: string;
+ content: string;
+ answer: string;
+ username: string;
+ company: string;
+ account: string;
+ phone: string;
+ email: string;
+ createTime: string;
+ answerTime: string;
+ isPublished: boolean;
+ isTop: boolean;
+ status: string;
+}
+
+const ReadQuestionManage: React.FC = () => {
+ const intl = useIntl();
+ const [loading, setLoading] = useState(false);
+ const [questionData, setQuestionData] = useState([]);
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+ const [form] = Form.useForm();
+ const [detailModalVisible, setDetailModalVisible] = useState(false);
+ const [currentQuestion, setCurrentQuestion] = useState(null);
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total: number) => `共 ${total} 条记录`,
+ });
+ const [searchParams, setSearchParams] = useState<{
+ title?: string;
+ category?: string;
+ }>({});
+
+ // 模拟数据
+ const mockData: QuestionItemType[] = [
+ {
+ key: '1',
+ id: '1',
+ title: '如何查询招标项目进度?',
+ category: '招标流程',
+ content: '我想了解如何在系统中查询我参与的招标项目的进度情况,有哪些步骤?',
+ answer: '您好,感谢您的提问。
查询招标项目进度的步骤如下:
- 登录系统后,点击顶部导航栏的"我的项目"
- 在左侧菜单选择"投标项目"
- 在项目列表中可以看到所有您参与的项目及其当前状态
- 点击具体项目可以查看详细的进度信息
如有更多问题,请随时咨询。
',
+ username: '张三',
+ company: '上海航运有限公司',
+ account: 'zhangsan2024',
+ phone: '13812345678',
+ email: 'zhangsan@example.com',
+ createTime: '2024-05-10 09:30:45',
+ answerTime: '2024-05-10 14:20:30',
+ isPublished: true,
+ isTop: true,
+ status: '1'
+ },
+ {
+ key: '2',
+ id: '2',
+ title: '投标文件格式有什么要求?',
+ category: '投标指南',
+ content: '请问投标文件需要什么格式?是否有模板可以参考?文件大小有限制吗?',
+ answer: '您好:
关于投标文件的要求如下:
1. 文件格式:PDF格式为主,技术文档可接受Word格式
2. 文件大小:单个文件不超过50MB
3. 模板下载:可在"资料下载"栏目中下载对应项目的投标文件模板
4. 命名规则:请按"公司名称-项目编号-文件类型"格式命名
请确保文件内容清晰可辨,签章完整。
',
+ username: '李四',
+ company: '北京建设工程有限公司',
+ account: 'lisi2024',
+ phone: '13987654321',
+ email: 'lisi@example.com',
+ createTime: '2024-05-12 11:20:15',
+ answerTime: '2024-05-12 16:45:22',
+ isPublished: true,
+ isTop: false,
+ status: '1'
+ },
+ {
+ key: '3',
+ id: '3',
+ title: '如何修改已提交的投标文件?',
+ category: '投标指南',
+ content: '我已经提交了投标文件,但发现有一处数据填写错误,请问如何修改?是否可以在截止日期前重新提交?',
+ answer: '尊敬的用户:
关于修改已提交投标文件的问题,解答如下:
在投标截止时间前,您可以通过以下步骤修改已提交的文件:
- 登录系统后进入"我的投标"页面
- 找到对应的项目,点击"撤回投标"按钮
- 系统会将投标状态改为"已撤回"
- 然后您可以点击"重新投标",上传修改后的文件
请注意,投标截止时间后将无法进行任何修改。建议您在提交前仔细核对所有信息。
',
+ username: '王五',
+ company: '广州海运集团',
+ account: 'wangwu2024',
+ phone: '13765432198',
+ email: 'wangwu@example.com',
+ createTime: '2024-05-15 14:30:00',
+ answerTime: '2024-05-15 17:10:45',
+ isPublished: false,
+ isTop: false,
+ status: '1'
+ },
+ {
+ key: '4',
+ id: '4',
+ title: '资质审核需要多长时间?',
+ category: '注册指南',
+ content: '我刚提交了供应商资质审核材料,请问大概需要多长时间能够完成审核?审核通过后是否会有通知?',
+ answer: '您好!
关于资质审核时间的问题,回复如下:
1. 一般情况下,资质审核会在3-5个工作日内完成
2. 如材料齐全且符合要求,可能会更快完成审核
3. 审核结果将通过以下方式通知您:
如超过5个工作日仍未收到审核结果,建议您联系客服热线:400-123-4567。
',
+ username: '赵六',
+ company: '深圳科技有限公司',
+ account: 'zhaoliu2024',
+ phone: '13612345678',
+ email: 'zhaoliu@example.com',
+ createTime: '2024-05-18 10:15:30',
+ answerTime: '2024-05-20 09:25:10',
+ isPublished: true,
+ isTop: true,
+ status: '1'
+ },
+ {
+ key: '5',
+ id: '5',
+ title: '如何查看招标结果公示?',
+ category: '招标流程',
+ content: '我参与了贵公司的一个招标项目,现在已经截止,请问在哪里可以查看招标结果公示?',
+ answer: '尊敬的用户:
查看招标结果公示的方法如下:
- 访问平台首页,在顶部导航栏选择"信息公开"
- 在下拉菜单中选择"中标结果公示"
- 可以通过项目编号、项目名称或招标单位进行搜索
- 也可以在"我的项目"中查看您参与项目的结果状态
根据相关规定,招标结果将在评标结束后的2个工作日内公示,公示期为3个工作日。
如有其他疑问,请随时咨询。
',
+ username: '钱七',
+ company: '天津贸易有限公司',
+ account: 'qianqi2024',
+ phone: '13898765432',
+ email: 'qianqi@example.com',
+ createTime: '2024-05-22 16:40:20',
+ answerTime: '2024-05-23 10:30:15',
+ isPublished: true,
+ isTop: false,
+ status: '1'
+ }
+ ];
+
+ // 获取已回答的用户提问列表数据(使用模拟数据)
+ const fetchQuestionList = (current: number = 1, pageSize: number = 10) => {
+ setLoading(true);
+
+ // 模拟API请求延迟
+ setTimeout(() => {
+ // 根据搜索条件过滤数据
+ let filteredData = [...mockData];
+ if (searchParams.title) {
+ filteredData = filteredData.filter(item =>
+ item.title.toLowerCase().includes(searchParams.title!.toLowerCase())
+ );
+ }
+ if (searchParams.category) {
+ filteredData = filteredData.filter(item =>
+ item.category === searchParams.category
+ );
+ }
+
+ // 计算分页数据
+ const startIndex = (current - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ const paginatedData = filteredData.slice(startIndex, endIndex);
+
+ setQuestionData(paginatedData);
+ setPagination({
+ ...pagination,
+ current,
+ pageSize,
+ total: filteredData.length,
+ });
+ setLoading(false);
+ }, 500);
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchQuestionList();
+ }, []);
+
+ // 处理搜索
+ const handleSearch = (values: any) => {
+ setSearchParams(values);
+ fetchQuestionList(1, pagination.pageSize);
+ };
+
+ // 处理查看详情
+ const handleView = (record: QuestionItemType) => {
+ setCurrentQuestion(record);
+ setDetailModalVisible(true);
+ };
+
+ // 处理发布状态切换
+ const handlePublishChange = (checked: boolean, record: QuestionItemType) => {
+ const newData = questionData.map(item => {
+ if (item.id === record.id) {
+ return { ...item, isPublished: checked };
+ }
+ return item;
+ });
+ setQuestionData(newData);
+ message.success(`${checked ? '发布' : '取消发布'}成功`);
+ };
+
+ // 处理置顶状态切换
+ const handleTopChange = (checked: boolean, record: QuestionItemType) => {
+ const newData = questionData.map(item => {
+ if (item.id === record.id) {
+ return { ...item, isTop: checked };
+ }
+ return item;
+ });
+ setQuestionData(newData);
+ message.success(`${checked ? '置顶' : '取消置顶'}成功`);
+ };
+
+ // 处理删除
+ const showDeleteConfirm = (record: QuestionItemType) => {
+ confirm({
+ title: intl.formatMessage({ id: 'readQuestion.confirmDelete' }),
+ icon: ,
+ content: `${intl.formatMessage({ id: 'readQuestion.question' })}: ${record.title}`,
+ okText: intl.formatMessage({ id: 'readQuestion.delete' }),
+ okType: 'danger',
+ cancelText: intl.formatMessage({ id: 'unreadQuestion.cancel' }),
+ maskClosable: false,
+ onOk: () => {
+ // 模拟删除操作
+ const newData = questionData.filter(item => item.id !== record.id);
+ setQuestionData(newData);
+ message.success('删除成功');
+ },
+ });
+ };
+
+ // 处理表格分页变化
+ const handleTableChange = (newPagination: any) => {
+ fetchQuestionList(newPagination.current, newPagination.pageSize);
+ };
+
+ // 处理批量删除
+ const handleBatchDelete = () => {
+ if (selectedRowKeys.length === 0) {
+ message.warning(intl.formatMessage({ id: 'readQuestion.selectRequired' }));
+ return;
+ }
+
+ confirm({
+ title: intl.formatMessage({ id: 'readQuestion.batchDelete' }),
+ icon: ,
+ content: intl.formatMessage({ id: 'readQuestion.batchDeleteConfirm' }),
+ okText: intl.formatMessage({ id: 'readQuestion.delete' }),
+ okType: 'danger',
+ cancelText: intl.formatMessage({ id: 'unreadQuestion.cancel' }),
+ maskClosable: false,
+ onOk: () => {
+ // 模拟批量删除操作
+ const newData = questionData.filter(item => !selectedRowKeys.includes(item.key));
+ setQuestionData(newData);
+ setSelectedRowKeys([]);
+ message.success('批量删除成功');
+ },
+ });
+ };
+
+ // 行选择限制
+ const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+ setSelectedRowKeys(newSelectedRowKeys);
+ };
+
+ const rowSelection = {
+ selectedRowKeys,
+ onChange: onSelectChange,
+ };
+
+ const hasSelected = selectedRowKeys.length > 0;
+
+ // 问题分类选项
+ const categoryOptions = [
+ { value: '招标流程', label: '招标流程' },
+ { value: '投标指南', label: '投标指南' },
+ { value: '注册指南', label: '注册指南' },
+ { value: '系统操作', label: '系统操作' },
+ { value: '其他问题', label: '其他问题' },
+ ];
+
+ // 表格列定义
+ const columns = [
+ {
+ title: '问题标题',
+ dataIndex: 'title',
+ key: 'title',
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (title: string, record: QuestionItemType) => (
+
+
+ {record.isTop && 置顶}
+ {title}
+
+
+ ),
+ },
+ {
+ title: '问题分类',
+ dataIndex: 'category',
+ key: 'category',
+ width: 120,
+ },
+ {
+ title: '提问人',
+ dataIndex: 'username',
+ key: 'username',
+ width: 100,
+ },
+ {
+ title: '公司',
+ dataIndex: 'company',
+ key: 'company',
+ width: 180,
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (company: string) => (
+
+ {company}
+
+ ),
+ },
+ {
+ title: '账号',
+ dataIndex: 'account',
+ key: 'account',
+ width: 120,
+ },
+ {
+ title: '联系方式',
+ dataIndex: 'phone',
+ key: 'phone',
+ width: 120,
+ },
+ {
+ title: '提问时间',
+ dataIndex: 'createTime',
+ key: 'createTime',
+ width: 150,
+ sorter: (a: QuestionItemType, b: QuestionItemType) =>
+ new Date(a.createTime).getTime() - new Date(b.createTime).getTime(),
+ },
+ {
+ title: '是否发布',
+ dataIndex: 'isPublished',
+ key: 'isPublished',
+ width: 100,
+ render: (isPublished: boolean, record: QuestionItemType) => (
+ handlePublishChange(checked, record)}
+ />
+ ),
+ },
+ {
+ title: '是否置顶',
+ dataIndex: 'isTop',
+ key: 'isTop',
+ width: 100,
+ render: (isTop: boolean, record: QuestionItemType) => (
+ handleTopChange(checked, record)}
+ />
+ ),
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 150,
+ render: (_: unknown, record: QuestionItemType) => (
+
+ } onClick={() => handleView(record)}>
+ 查看
+
+
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+ }>
+ 搜索
+
+
+
+
+
+
+ }
+ onClick={handleBatchDelete}
+ disabled={!hasSelected}
+ loading={loading}
+ >
+ 批量删除
+
+ {hasSelected && (
+
+ 已选择 {selectedRowKeys.length} 项
+
+ )}
+
+
+
+
+
+ {/* 查看详情模态框 */}
+
setDetailModalVisible(false)}
+ footer={[
+ ,
+ ]}
+ width={800}
+ maskClosable={false}
+ >
+ {currentQuestion && (
+
+
+
{currentQuestion.title}
+
+ 分类:{currentQuestion.category}
+ 提问人:{currentQuestion.username}
+ 提问时间:{currentQuestion.createTime}
+
+
+
+
+
问题内容
+
{currentQuestion.content}
+
+
+
+
+
+
+
+ 是否发布:
+ {
+ setCurrentQuestion({ ...currentQuestion, isPublished: checked });
+ handlePublishChange(checked, currentQuestion);
+ }}
+ />
+
+
+ 是否置顶:
+ {
+ setCurrentQuestion({ ...currentQuestion, isTop: checked });
+ handleTopChange(checked, currentQuestion);
+ }}
+ />
+
+
+
+ 回答时间:{currentQuestion.answerTime}
+
+
+
+ )}
+
+
+ );
+};
+
+export default ReadQuestionManage;
diff --git a/src/pages/userQuestionManage/unreadQuestionManage.tsx b/src/pages/userQuestionManage/unreadQuestionManage.tsx
new file mode 100644
index 0000000..b97ef1c
--- /dev/null
+++ b/src/pages/userQuestionManage/unreadQuestionManage.tsx
@@ -0,0 +1,523 @@
+import React, { useState, useEffect } from 'react';
+import { useIntl } from 'umi';
+import { Button, Table, Modal, message, Input, Form, Tooltip, Tag, Space, Switch } from 'antd';
+import {
+ DeleteOutlined,
+ ExclamationCircleOutlined,
+ SearchOutlined,
+ EditOutlined
+} from '@ant-design/icons';
+import '../userQuestionManage/userQuestionManage.less';
+// 引入封装的WangEditor组件
+import WangEditor from '@/components/WangEidtor/WangEidtor';
+
+const { TextArea } = Input;
+const { confirm } = Modal;
+
+// 用户提问类型定义
+interface QuestionItemType {
+ key: string;
+ id: string;
+ title: string;
+ category: string;
+ content: string;
+ answer: string;
+ username: string;
+ company: string;
+ account: string;
+ phone: string;
+ email: string;
+ createTime: string;
+ isPublished: boolean;
+ isTop: boolean;
+ status: string;
+}
+
+const UnreadQuestionManage: React.FC = () => {
+ const intl = useIntl();
+ const [loading, setLoading] = useState(false);
+ const [questionData, setQuestionData] = useState([]);
+ const [selectedRowKeys, setSelectedRowKeys] = useState([]);
+ const [form] = Form.useForm();
+ const [answerForm] = Form.useForm();
+ const [answerModalVisible, setAnswerModalVisible] = useState(false);
+ const [currentQuestion, setCurrentQuestion] = useState(null);
+ const [answerContent, setAnswerContent] = useState('');
+ const [pagination, setPagination] = useState({
+ current: 1,
+ pageSize: 10,
+ total: 0,
+ showSizeChanger: true,
+ showQuickJumper: true,
+ showTotal: (total: number) => `共 ${total} 条记录`,
+ });
+ const [searchParams, setSearchParams] = useState<{
+ title?: string;
+ category?: string;
+ }>({});
+
+ // 模拟数据
+ const mockData: QuestionItemType[] = [
+ {
+ key: '1',
+ id: '1',
+ title: '如何获取招标文件?',
+ category: '招标流程',
+ content: '我是新注册的用户,想参与一个招标项目,但不知道如何获取招标文件,请问有哪些途径可以获取?是否需要支付费用?',
+ answer: '',
+ username: '刘一',
+ company: '杭州科技有限公司',
+ account: 'liuyi2024',
+ phone: '13712345678',
+ email: 'liuyi@example.com',
+ createTime: '2024-05-24 09:15:30',
+ isPublished: false,
+ isTop: false,
+ status: '0'
+ },
+ {
+ key: '2',
+ id: '2',
+ title: '供应商资质认证流程是什么?',
+ category: '注册指南',
+ content: '我们公司想成为贵平台的供应商,请问资质认证需要准备哪些材料?整个流程大概需要多长时间?',
+ answer: '',
+ username: '陈二',
+ company: '南京电子科技有限公司',
+ account: 'chener2024',
+ phone: '13887654321',
+ email: 'chener@example.com',
+ createTime: '2024-05-23 14:20:15',
+ isPublished: false,
+ isTop: false,
+ status: '0'
+ },
+ {
+ key: '3',
+ id: '3',
+ title: '系统登录异常如何解决?',
+ category: '系统操作',
+ content: '最近几天登录系统时经常提示"网络异常,请稍后再试",但我的网络连接正常,请问这是什么原因?如何解决?',
+ answer: '',
+ username: '张三',
+ company: '上海航运有限公司',
+ account: 'zhangsan2024',
+ phone: '13812345678',
+ email: 'zhangsan@example.com',
+ createTime: '2024-05-22 16:45:20',
+ isPublished: false,
+ isTop: false,
+ status: '0'
+ },
+ {
+ key: '4',
+ id: '4',
+ title: '如何修改企业信息?',
+ category: '系统操作',
+ content: '我们公司最近更换了法人代表和公司地址,需要在系统中更新这些信息,请问应该如何操作?是否需要重新提交认证材料?',
+ answer: '',
+ username: '李四',
+ company: '北京建设工程有限公司',
+ account: 'lisi2024',
+ phone: '13987654321',
+ email: 'lisi@example.com',
+ createTime: '2024-05-21 11:30:45',
+ isPublished: false,
+ isTop: false,
+ status: '0'
+ },
+ {
+ key: '5',
+ id: '5',
+ title: '投标保证金如何缴纳?',
+ category: '投标指南',
+ content: '我想参与一个工程招标项目,要求缴纳投标保证金,请问保证金应该如何缴纳?有哪些注意事项?缴纳后如何确认到账?',
+ answer: '',
+ username: '王五',
+ company: '广州海运集团',
+ account: 'wangwu2024',
+ phone: '13765432198',
+ email: 'wangwu@example.com',
+ createTime: '2024-05-20 09:25:10',
+ isPublished: false,
+ isTop: false,
+ status: '0'
+ }
+ ];
+
+ // 获取未回答的用户提问列表数据(使用模拟数据)
+ const fetchQuestionList = (current: number = 1, pageSize: number = 10) => {
+ setLoading(true);
+
+ // 模拟API请求延迟
+ setTimeout(() => {
+ // 根据搜索条件过滤数据
+ let filteredData = [...mockData];
+ if (searchParams.title) {
+ filteredData = filteredData.filter(item =>
+ item.title.toLowerCase().includes(searchParams.title!.toLowerCase())
+ );
+ }
+ if (searchParams.category) {
+ filteredData = filteredData.filter(item =>
+ item.category === searchParams.category
+ );
+ }
+
+ // 计算分页数据
+ const startIndex = (current - 1) * pageSize;
+ const endIndex = startIndex + pageSize;
+ const paginatedData = filteredData.slice(startIndex, endIndex);
+
+ setQuestionData(paginatedData);
+ setPagination({
+ ...pagination,
+ current,
+ pageSize,
+ total: filteredData.length,
+ });
+ setLoading(false);
+ }, 500);
+ };
+
+ // 首次加载时获取数据
+ useEffect(() => {
+ fetchQuestionList();
+ }, []);
+
+ // 处理搜索
+ const handleSearch = (values: any) => {
+ setSearchParams(values);
+ fetchQuestionList(1, pagination.pageSize);
+ };
+
+ // 处理回答问题
+ const handleAnswer = (record: QuestionItemType) => {
+ setCurrentQuestion(record);
+ setAnswerContent('');
+ answerForm.resetFields();
+ setAnswerModalVisible(true);
+ };
+
+ // 处理提交回答
+ const handleSubmitAnswer = () => {
+ if (!answerContent || answerContent.trim() === '') {
+ message.error('请输入回答内容');
+ return;
+ }
+
+ // 模拟提交回答
+ if (currentQuestion) {
+ // 在实际应用中,这里会调用API提交回答
+ message.success('回答提交成功');
+
+ // 从列表中移除已回答的问题
+ const newData = questionData.filter(item => item.id !== currentQuestion.id);
+ setQuestionData(newData);
+
+ setAnswerModalVisible(false);
+ setAnswerContent('');
+ }
+ };
+
+ // 处理删除
+ const showDeleteConfirm = (record: QuestionItemType) => {
+ confirm({
+ title: intl.formatMessage({ id: 'unreadQuestion.confirmDelete' }),
+ icon: ,
+ content: `${intl.formatMessage({ id: 'unreadQuestion.question' })}: ${record.title}`,
+ okText: intl.formatMessage({ id: 'unreadQuestion.delete' }),
+ okType: 'danger',
+ cancelText: intl.formatMessage({ id: 'unreadQuestion.cancel' }),
+ maskClosable: false,
+ onOk: () => {
+ // 模拟删除操作
+ const newData = questionData.filter(item => item.id !== record.id);
+ setQuestionData(newData);
+ message.success('删除成功');
+ },
+ });
+ };
+
+ // 处理表格分页变化
+ const handleTableChange = (newPagination: any) => {
+ fetchQuestionList(newPagination.current, newPagination.pageSize);
+ };
+
+ // 处理批量删除
+ const handleBatchDelete = () => {
+ if (selectedRowKeys.length === 0) {
+ message.warning(intl.formatMessage({ id: 'unreadQuestion.selectRequired' }));
+ return;
+ }
+
+ confirm({
+ title: intl.formatMessage({ id: 'unreadQuestion.batchDelete' }),
+ icon: ,
+ content: intl.formatMessage({ id: 'unreadQuestion.batchDeleteConfirm' }),
+ okText: intl.formatMessage({ id: 'unreadQuestion.delete' }),
+ okType: 'danger',
+ cancelText: intl.formatMessage({ id: 'unreadQuestion.cancel' }),
+ maskClosable: false,
+ onOk: () => {
+ // 模拟批量删除操作
+ const newData = questionData.filter(item => !selectedRowKeys.includes(item.key));
+ setQuestionData(newData);
+ setSelectedRowKeys([]);
+ message.success('批量删除成功');
+ },
+ });
+ };
+
+ // 行选择限制
+ const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
+ setSelectedRowKeys(newSelectedRowKeys);
+ };
+
+ const rowSelection = {
+ selectedRowKeys,
+ onChange: onSelectChange,
+ };
+
+ const hasSelected = selectedRowKeys.length > 0;
+
+ // 问题分类选项
+ const categoryOptions = [
+ { value: '招标流程', label: '招标流程' },
+ { value: '投标指南', label: '投标指南' },
+ { value: '注册指南', label: '注册指南' },
+ { value: '系统操作', label: '系统操作' },
+ { value: '其他问题', label: '其他问题' },
+ ];
+
+ // 表格列定义
+ const columns = [
+ {
+ title: '问题标题',
+ dataIndex: 'title',
+ key: 'title',
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (title: string) => (
+
+ {title}
+
+ ),
+ },
+ {
+ title: '问题分类',
+ dataIndex: 'category',
+ key: 'category',
+ width: 120,
+ },
+ {
+ title: '提问人',
+ dataIndex: 'username',
+ key: 'username',
+ width: 100,
+ },
+ {
+ title: '公司',
+ dataIndex: 'company',
+ key: 'company',
+ width: 180,
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (company: string) => (
+
+ {company}
+
+ ),
+ },
+ {
+ title: '账号',
+ dataIndex: 'account',
+ key: 'account',
+ width: 120,
+ },
+ {
+ title: '联系方式',
+ dataIndex: 'phone',
+ key: 'phone',
+ width: 120,
+ },
+ {
+ title: '邮箱',
+ dataIndex: 'email',
+ key: 'email',
+ width: 180,
+ ellipsis: {
+ showTitle: false,
+ },
+ render: (email: string) => (
+
+ {email}
+
+ ),
+ },
+ {
+ title: '提问时间',
+ dataIndex: 'createTime',
+ key: 'createTime',
+ width: 150,
+ sorter: (a: QuestionItemType, b: QuestionItemType) =>
+ new Date(a.createTime).getTime() - new Date(b.createTime).getTime(),
+ },
+ {
+ title: '操作',
+ key: 'action',
+ width: 150,
+ render: (_: unknown, record: QuestionItemType) => (
+
+ } onClick={() => handleAnswer(record)}>
+ 回答
+
+
+
+ ),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+ }>
+ 搜索
+
+
+
+
+
+
+ }
+ onClick={handleBatchDelete}
+ disabled={!hasSelected}
+ loading={loading}
+ >
+ 批量删除
+
+ {hasSelected && (
+
+ 已选择 {selectedRowKeys.length} 项
+
+ )}
+
+
+
+
+
+ {/* 回答问题模态框 */}
+
setAnswerModalVisible(false)}
+ footer={[
+ ,
+ ,
+ ]}
+ width={800}
+ maskClosable={false}
+ >
+ {currentQuestion && (
+
+
+
{currentQuestion.title}
+
+ 分类:{currentQuestion.category}
+ 提问人:{currentQuestion.username}
+ 提问时间:{currentQuestion.createTime}
+
+
+
+
+
问题内容
+
{currentQuestion.content}
+
+
+
+
+ )}
+
+
+ );
+};
+
+export default UnreadQuestionManage;
diff --git a/src/pages/userQuestionManage/userQuestionManage.less b/src/pages/userQuestionManage/userQuestionManage.less
new file mode 100644
index 0000000..f62cac8
--- /dev/null
+++ b/src/pages/userQuestionManage/userQuestionManage.less
@@ -0,0 +1,198 @@
+.common-container {
+ background: #fff;
+ border-radius: 4px;
+ padding: 24px;
+ min-height: calc(100vh - 184px);
+
+ .filter-action-row {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 16px;
+ flex-wrap: wrap;
+
+ .filter-form {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 16px;
+
+ .filter-btns {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 0;
+ }
+ }
+
+ .right-buttons {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+
+ .selected-count {
+ color: #999;
+ }
+ }
+ }
+
+ .content-area {
+ margin-top: 16px;
+ }
+
+ .card-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 24px;
+ margin-top: 24px;
+
+ .stat-card {
+ width: calc(25% - 18px);
+ min-width: 250px;
+ height: 120px;
+ border-radius: 8px;
+ padding: 16px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.09);
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ cursor: pointer;
+ transition: all 0.3s;
+
+ &:hover {
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
+ transform: translateY(-2px);
+ }
+
+ .card-title {
+ font-size: 16px;
+ color: rgba(0, 0, 0, 0.85);
+ margin-bottom: 8px;
+ }
+
+ .card-value {
+ font-size: 28px;
+ font-weight: bold;
+ color: #1890ff;
+ }
+
+ .card-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 8px;
+
+ .card-label {
+ font-size: 14px;
+ color: rgba(0, 0, 0, 0.45);
+ }
+ }
+ }
+ }
+
+ // 问题详情样式
+ .question-detail-header {
+ margin-bottom: 20px;
+ border-bottom: 1px solid #f0f0f0;
+ padding-bottom: 16px;
+
+ h2 {
+ margin-bottom: 12px;
+ font-size: 18px;
+ font-weight: 600;
+ color: #333;
+ }
+
+ .question-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 16px;
+ color: #666;
+ font-size: 14px;
+
+ span {
+ display: inline-flex;
+ align-items: center;
+ }
+ }
+ }
+
+ .question-detail-section {
+ margin-bottom: 24px;
+
+ h3 {
+ font-size: 16px;
+ font-weight: 500;
+ margin-bottom: 12px;
+ color: #333;
+ }
+
+ .question-detail-content {
+ background-color: #f9f9f9;
+ padding: 16px;
+ border-radius: 4px;
+ white-space: pre-wrap;
+ line-height: 1.6;
+ }
+
+ .answer-content-wrapper {
+ border: 1px solid #f0f0f0;
+ border-radius: 4px;
+ padding: 16px;
+ min-height: 200px;
+
+ .answer-content {
+ line-height: 1.6;
+
+ p {
+ margin-bottom: 12px;
+ }
+
+ ul, ol {
+ padding-left: 20px;
+ margin-bottom: 12px;
+ }
+ }
+ }
+ }
+
+ .question-detail-footer {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-top: 24px;
+ padding-top: 16px;
+ border-top: 1px solid #f0f0f0;
+
+ .question-settings {
+ display: flex;
+ gap: 24px;
+
+ .setting-item {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ }
+
+ .answer-info {
+ color: #666;
+ font-size: 14px;
+ }
+ }
+
+ .question-settings {
+ display: flex;
+ gap: 40px;
+ margin-top: 16px;
+
+ .setting-item {
+ display: flex;
+ align-items: center;
+ }
+ }
+
+ :global {
+ .ant-form-item {
+ margin-bottom: 16px;
+ }
+ }
+}
diff --git a/src/pages/userQuestionManage/userQuestionManage.tsx b/src/pages/userQuestionManage/userQuestionManage.tsx
new file mode 100644
index 0000000..9720728
--- /dev/null
+++ b/src/pages/userQuestionManage/userQuestionManage.tsx
@@ -0,0 +1,84 @@
+import React from 'react';
+import { useIntl, history } from 'umi';
+import { Card, Row, Col, Button, Statistic } from 'antd';
+import { MessageOutlined, CheckCircleOutlined, QuestionCircleOutlined } from '@ant-design/icons';
+import './userQuestionManage.less';
+
+const UserQuestionManage: React.FC = () => {
+ const intl = useIntl();
+
+ // 跳转到已阅问题页面
+ const goToReadQuestions = () => {
+ history.push('/readQuestionManage');
+ };
+
+ // 跳转到未阅问题页面
+ const goToUnreadQuestions = () => {
+ history.push('/unreadQuestionManage');
+ };
+
+ return (
+
+
{intl.formatMessage({ id: 'userQuestion.title' })}
+
+
+
+
+ }
+ suffix="条"
+ />
+
+
+
+
+
+
+
+
+ }
+ suffix="条"
+ />
+
+
+
+
+
+
+
+
+ }
+ suffix="条"
+ />
+
+
+
+
+
+
+
+
+
{intl.formatMessage({ id: 'userQuestion.description' })}
+
{intl.formatMessage({ id: 'userQuestion.descriptionText' })}
+
+
+ );
+};
+
+export default UserQuestionManage;
diff --git a/src/servers/api/about.ts b/src/servers/api/about.ts
new file mode 100644
index 0000000..17d9baa
--- /dev/null
+++ b/src/servers/api/about.ts
@@ -0,0 +1,79 @@
+import request from '@/utils/request';
+
+export interface AboutUsRequest {
+ /**
+ * 地址
+ */
+ address: number;
+ /**
+ * 地址_英文版
+ */
+ addressEn: string;
+ /**
+ * 地址地图图片
+ */
+ addressImg: string;
+ /**
+ * 咨询联系方式
+ */
+ contactsConsult: string;
+ /**
+ * 咨询联系方式_英文版
+ */
+ contactsConsultEn: string;
+ /**
+ * 联系邮箱
+ */
+ contactsEmail: string;
+ /**
+ * 联系邮箱_英文版
+ */
+ contactsEmailEn: string;
+ /**
+ * 联系方式
+ */
+ contactsPhone: string;
+ /**
+ * 联系方式_英文版
+ */
+ contactsPhoneEn: string;
+ /**
+ * 内容
+ */
+ content: string;
+ /**
+ * 内容_英文版
+ */
+ contentEn: string;
+ /**
+ * 标题
+ */
+ title: string;
+ /**
+ * 标题_英文版
+ */
+ titleEn: string;
+ [property: string]: any;
+}
+
+interface ResponseData {
+ code: string;
+ data: T;
+ message: string;
+ success: boolean;
+}
+
+// 获取关于我们详情
+export async function getAboutUs() {
+ return request>('/portals/us/now', {
+ method: 'GET',
+ });
+}
+
+// 更新关于我们
+export async function updateAboutUs(params: AboutUsRequest) {
+ return request>('/portals/us', {
+ method: 'POST',
+ data: params,
+ });
+}
diff --git a/src/servers/api/help.ts b/src/servers/api/help.ts
new file mode 100644
index 0000000..faaff14
--- /dev/null
+++ b/src/servers/api/help.ts
@@ -0,0 +1,62 @@
+import request from '@/utils/request';
+
+// 获取帮助中心列表
+export async function getHelpList(params: API.HelpSearchParams) {
+ return request('/api/help/list', {
+ method: 'POST',
+ data: {
+ basePageRequest: {
+ pageNo: params.pageNo || 1,
+ pageSize: params.pageSize || 10,
+ },
+ ...params,
+ },
+ });
+}
+
+// 添加帮助
+export async function addHelp(params: API.HelpRequest) {
+ return request('/api/help/add', {
+ method: 'POST',
+ data: params,
+ });
+}
+
+// 更新帮助
+export async function updateHelp(id: string, params: API.HelpRequest) {
+ return request(`/api/help/update/${id}`, {
+ method: 'PUT',
+ data: params,
+ });
+}
+
+// 删除帮助
+export async function deleteHelp(id: string) {
+ return request(`/api/help/delete/${id}`, {
+ method: 'DELETE',
+ });
+}
+
+// 批量删除帮助
+export async function batchDeleteHelp(ids: string[]) {
+ return request('/api/help/batchDelete', {
+ method: 'DELETE',
+ data: { ids },
+ });
+}
+
+// 更新帮助状态(发布/下架)
+export async function updateHelpStatus(id: string, status: string) {
+ return request(`/api/help/updateStatus/${id}`, {
+ method: 'PUT',
+ data: { status },
+ });
+}
+
+// 更新帮助置顶状态
+export async function updateHelpTopStatus(id: string, isTop: string) {
+ return request(`/api/help/updateTop/${id}`, {
+ method: 'PUT',
+ data: { isTop },
+ });
+}
diff --git a/src/servers/api/notice.ts b/src/servers/api/notice.ts
new file mode 100644
index 0000000..d60d313
--- /dev/null
+++ b/src/servers/api/notice.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+
+// 获取通知列表
+export async function getNoticeList(params: API.NoticeSearchParams) {
+ return request('/api/notices', {
+ method: 'GET',
+ params,
+ });
+}
+
+// 获取通知详情
+export async function getNoticeDetail(id: string) {
+ return request(`/api/notices/${id}`, {
+ method: 'GET',
+ });
+}
+
+// 新增通知
+export async function addNotice(params: API.NoticeRequest) {
+ return request('/api/notices', {
+ method: 'POST',
+ data: params,
+ });
+}
+
+// 更新通知
+export async function updateNotice(id: string, params: API.NoticeRequest) {
+ return request(`/api/notices/${id}`, {
+ method: 'PUT',
+ data: params,
+ });
+}
+
+// 删除通知
+export async function deleteNotice(id: string) {
+ return request(`/api/notices/${id}`, {
+ method: 'DELETE',
+ });
+}
+
+// 批量删除通知
+export async function batchDeleteNotice(ids: string[]) {
+ return request('/api/notices/batch', {
+ method: 'DELETE',
+ data: { ids },
+ });
+}
+
+// 发布/下架通知
+export async function updateNoticeStatus(id: string, status: string) {
+ return request(`/api/notices/${id}/status`, {
+ method: 'PUT',
+ data: { status },
+ });
+}
+
+// 置顶/取消置顶通知
+export async function updateNoticeTopStatus(id: string, isTop: string) {
+ return request(`/api/notices/${id}/top`, {
+ method: 'PUT',
+ data: { isTop },
+ });
+}
diff --git a/src/servers/api/typings.d.ts b/src/servers/api/typings.d.ts
index c2bdf4c..c973415 100644
--- a/src/servers/api/typings.d.ts
+++ b/src/servers/api/typings.d.ts
@@ -256,4 +256,108 @@ declare namespace API {
fileType: string;
fileUrl: string;
}
+
+ // 通知中心数据项
+ export interface NoticeRecord {
+ id: string;
+ title: string;
+ titleEn?: string;
+ content: string;
+ contentEn?: string;
+ createTime: string;
+ createBy: string;
+ status: string; // 状态:0-草稿,1-已发布,2-已下架
+ isTop: string; // 是否置顶:0-否,1-是
+ settingEn?: number; // 是否设置英文:0-否,1-是
+ }
+
+ // 通知中心请求参数
+ export interface NoticeRequest {
+ id?: string;
+ title: string;
+ titleEn?: string;
+ content: string;
+ contentEn?: string;
+ isTop: string;
+ settingEn?: number;
+ }
+
+ // 通知中心查询参数
+ export interface NoticeSearchParams {
+ title?: string;
+ status?: string;
+ }
+
+ // 帮助中心数据项
+ export interface HelpRecord {
+ id: string;
+ title: string;
+ titleEn?: string;
+ type: string; // 类型,对应页面中的category
+ content: string;
+ contentEn?: string;
+ createTime: string;
+ createBy: string;
+ updateTime?: string;
+ updateBy?: string;
+ status: string; // 状态:0-草稿,1-已发布,2-已下架
+ isTop: string; // 是否置顶:0-否,1-是
+ settingEn: number; // 是否设置英文:0-否,1-是
+ answerContent?: string;
+ answerContentNe?: string;
+ publishBy?: string | null;
+ publishTime?: string | null;
+ deleteFlag?: null;
+ delFlag?: string;
+ tenantId?: string | null;
+ tenantName?: string | null;
+ lastUpdateTime?: null;
+ createDate?: null;
+ updateDate?: null;
+ basePageRequest?: null;
+ }
+
+ // 帮助中心请求参数
+ export interface HelpRequest {
+ id?: string;
+ title: string;
+ titleEn?: string;
+ type: string; // 类型,对应页面中的category
+ content: string;
+ contentEn?: string;
+ isTop: string;
+ settingEn: number;
+ answerContent?: string;
+ answerContentNe?: number;
+ }
+
+ // 帮助中心查询参数
+ export interface HelpSearchParams {
+ title?: string;
+ type?: string; // 类型,对应页面中的category
+ status?: string;
+ pageNo?: number;
+ pageSize?: number;
+ }
+
+ // 用户提问数据项
+ export interface UserQuestionRecord {
+ id: string;
+ userId: string;
+ username: string;
+ email?: string;
+ question: string;
+ answer?: string;
+ createTime: string;
+ answerTime?: string;
+ status: string; // 状态:0-未回答,1-已回答
+ }
+
+ // 用户提问查询参数
+ export interface UserQuestionSearchParams {
+ question?: string;
+ status?: string;
+ pageNo?: number;
+ pageSize?: number;
+ }
}
diff --git a/src/servers/api/userQuestion.ts b/src/servers/api/userQuestion.ts
new file mode 100644
index 0000000..d7051c6
--- /dev/null
+++ b/src/servers/api/userQuestion.ts
@@ -0,0 +1,75 @@
+import request from '@/utils/request';
+
+// 获取用户提问列表
+export async function getUserQuestions(params: API.UserQuestionSearchParams) {
+ return request('/api/userQuestion/list', {
+ method: 'POST',
+ data: {
+ basePageRequest: {
+ pageNo: params.pageNo || 1,
+ pageSize: params.pageSize || 10,
+ },
+ ...params,
+ },
+ });
+}
+
+// 获取已回答的用户提问列表
+export async function getAnsweredQuestions(params: API.UserQuestionSearchParams) {
+ return request('/api/userQuestion/answered', {
+ method: 'POST',
+ data: {
+ basePageRequest: {
+ pageNo: params.pageNo || 1,
+ pageSize: params.pageSize || 10,
+ },
+ ...params,
+ status: '1', // 已回答状态
+ },
+ });
+}
+
+// 获取未回答的用户提问列表
+export async function getUnansweredQuestions(params: API.UserQuestionSearchParams) {
+ return request('/api/userQuestion/unanswered', {
+ method: 'POST',
+ data: {
+ basePageRequest: {
+ pageNo: params.pageNo || 1,
+ pageSize: params.pageSize || 10,
+ },
+ ...params,
+ status: '0', // 未回答状态
+ },
+ });
+}
+
+// 回复用户提问
+export async function replyQuestion(id: string, answer: string) {
+ return request(`/api/userQuestion/reply/${id}`, {
+ method: 'PUT',
+ data: { answer },
+ });
+}
+
+// 删除用户提问
+export async function deleteQuestion(id: string) {
+ return request(`/api/userQuestion/delete/${id}`, {
+ method: 'DELETE',
+ });
+}
+
+// 批量删除用户提问
+export async function batchDeleteQuestions(ids: string[]) {
+ return request('/api/userQuestion/batchDelete', {
+ method: 'DELETE',
+ data: { ids },
+ });
+}
+
+// 获取用户提问详情
+export async function getQuestionDetail(id: string) {
+ return request(`/api/userQuestion/detail/${id}`, {
+ method: 'GET',
+ });
+}