Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
C
center-book
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
center-book
Commits
8d6aaa09
提交
8d6aaa09
authored
7月 20, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refactor(admin): migrate books question bank module
上级
594f41d3
隐藏空白字符变更
内嵌
并排
正在显示
7 个修改的文件
包含
186 行增加
和
2 行删除
+186
-2
api.ts
apps/admin/src/modules/books/question-bank/api.ts
+39
-0
QuestionForm.tsx
...c/modules/books/question-bank/components/QuestionForm.tsx
+41
-0
query.ts
apps/admin/src/modules/books/question-bank/query.ts
+29
-0
routes.tsx
apps/admin/src/modules/books/question-bank/routes.tsx
+5
-0
useQuestionBankList.ts
...in/src/modules/books/question-bank/useQuestionBankList.ts
+29
-0
QuestionBankView.tsx
...rc/modules/books/question-bank/views/QuestionBankView.tsx
+41
-0
routes.tsx
apps/admin/src/modules/books/routes.tsx
+2
-2
没有找到文件。
apps/admin/src/modules/books/question-bank/api.ts
0 → 100644
浏览文件 @
8d6aaa09
import
{
message
}
from
"antd"
;
import
request
from
"@/utils/request"
;
import
type
{
QuestionBankQuery
}
from
"./query"
;
async
function
post
(
path
:
string
,
data
:
Record
<
string
,
unknown
>
=
{},
notify
=
false
)
{
try
{
const
response
=
(
await
request
.
post
(
path
,
new
URLSearchParams
(
data
as
Record
<
string
,
string
>
).
toString
(),
))
as
{
data
?:
{
code
?:
number
;
data
?:
any
;
message
?:
string
}
};
if
(
response
.
data
?.
code
===
200
)
{
if
(
notify
)
message
.
success
(
response
.
data
.
message
);
return
response
.
data
.
data
;
}
message
.
error
(
response
.
data
?.
message
||
"数据获取失败!"
);
}
catch
{}
return
false
;
}
export
const
getQuestionList
=
(
query
:
QuestionBankQuery
)
=>
post
(
"/book/question/getList"
,
query
);
export
const
getQuestion
=
(
id
:
string
|
number
)
=>
post
(
"/book/question/getInfoById"
,
{
id
});
export
const
addQuestion
=
(
data
:
Record
<
string
,
unknown
>
)
=>
post
(
"/book/question/add"
,
data
,
true
);
export
const
editQuestion
=
(
data
:
Record
<
string
,
unknown
>
)
=>
post
(
"/book/question/edit"
,
data
,
true
);
export
const
deleteQuestion
=
(
id
:
string
|
number
)
=>
post
(
"/book/question/del"
,
{
id
},
true
);
export
const
addQuestionType
=
(
typename
:
string
)
=>
post
(
"/book/question/type/add"
,
{
typename
},
true
);
export
const
getQuestionTypes
=
()
=>
post
(
"/book/question/type/getList"
,
{
page
:
1
,
page_size
:
999
});
export
const
getBookNames
=
()
=>
post
(
"book/teacher/getListAll"
);
export
async
function
importQuestions
(
file
:
File
)
{
try
{
const
form
=
new
FormData
();
form
.
append
(
"file"
,
file
);
const
response
=
(
await
request
.
post
(
"/book/question/import"
,
form
,
{
headers
:
{
"Content-Type"
:
"multipart/form-data"
},
}))
as
{
data
?:
{
code
?:
number
;
data
?:
any
;
message
?:
string
}
};
if
(
response
.
data
?.
code
===
200
)
return
{
msg
:
response
.
data
.
message
,
data
:
response
.
data
.
data
};
message
.
error
(
response
.
data
?.
message
||
"数据获取失败!"
);
}
catch
{}
return
false
;
}
apps/admin/src/modules/books/question-bank/components/QuestionForm.tsx
0 → 100644
浏览文件 @
8d6aaa09
import
{
Button
,
Checkbox
,
Drawer
,
Form
,
Input
,
Select
,
Space
}
from
"antd"
;
import
{
MinusOutlined
,
PlusOutlined
}
from
"@ant-design/icons"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
addQuestion
,
addQuestionType
,
editQuestion
}
from
"../api"
;
type
Props
=
{
open
:
boolean
;
onClose
:
()
=>
void
;
question
:
any
;
types
:
any
[];
books
:
any
[];
onTypesChange
:
()
=>
void
;
onSaved
:
()
=>
void
};
const
styles
=
[
"单选题"
,
"多选题"
,
"判断题"
,
"填空题"
,
"简答题"
];
export
default
function
QuestionForm
({
open
,
onClose
,
question
,
types
,
books
,
onTypesChange
,
onSaved
}:
Props
)
{
const
[
form
]
=
Form
.
useForm
();
const
[
addingType
,
setAddingType
]
=
useState
(
false
);
const
[
typeName
,
setTypeName
]
=
useState
(
""
);
const
questionStyle
=
Form
.
useWatch
(
"question_style"
,
form
);
useEffect
(()
=>
{
if
(
!
open
)
return
;
let
values
:
any
=
question
||
{
titles
:
""
,
type_id
:
undefined
,
question_style
:
undefined
,
analysis
:
""
,
answers
:
""
,
question_list
:
[],
book_id
:
undefined
};
if
(
typeof
values
.
question_list
===
"string"
)
{
try
{
values
=
{
...
values
,
question_list
:
JSON
.
parse
(
values
.
question_list
)
};
}
catch
{
values
=
{
...
values
,
question_list
:
[]
};
}
}
form
.
setFieldsValue
(
values
);
},
[
form
,
open
,
question
]);
const
addType
=
async
()
=>
{
const
result
:
any
=
await
addQuestionType
(
typeName
);
if
(
result
)
{
await
onTypesChange
();
form
.
setFieldValue
(
"type_id"
,
result
.
type_id
);
setTypeName
(
""
);
setAddingType
(
false
);
}
};
const
submit
=
async
(
values
:
any
)
=>
{
if
([
1
,
2
,
3
].
includes
(
values
.
question_style
))
{
const
answers
=
values
.
question_list
||
[];
const
correct
=
answers
.
filter
((
item
:
any
)
=>
item
.
correct
).
length
;
if
(([
1
,
3
].
includes
(
values
.
question_style
)
&&
correct
!==
1
)
||
(
values
.
question_style
===
2
&&
correct
<
2
))
{
form
.
setFields
([{
name
:
[
"question_list"
,
Math
.
max
(
0
,
answers
.
length
-
1
),
"correct"
],
errors
:
[
values
.
question_style
===
2
?
"多选题需要2个及以上正确答案!"
:
`
${
values
.
question_style
===
3
?
"判断题"
:
"单选题"
}
有且仅有一个正确答案!`
]
}]);
return
;
}
values
.
question_list
=
JSON
.
stringify
(
answers
);
}
const
ok
=
question
?.
id
?
await
editQuestion
({
...
values
,
id
:
question
.
id
})
:
await
addQuestion
(
values
);
if
(
ok
)
{
onClose
();
onSaved
();
}
};
const
needsOptions
=
[
1
,
2
,
3
].
includes
(
questionStyle
);
return
<
Drawer
open=
{
open
}
mask=
{
false
}
footer=
{
null
}
onClose=
{
onClose
}
width=
{
720
}
>
<
Form
form=
{
form
}
onFinish=
{
submit
}
labelCol=
{
{
span
:
5
}
}
wrapperCol=
{
{
span
:
19
}
}
>
<
Form
.
Item
name=
"book_id"
label=
"书籍名称"
rules=
{
[{
required
:
true
,
message
:
"请选择书籍名称"
}]
}
><
Select
showSearch
optionFilterProp=
"children"
placeholder=
"请选择书籍名称"
>
{
books
.
map
((
book
)
=>
<
Select
.
Option
key=
{
book
.
book_id
}
value=
{
book
.
book_id
}
>
{
book
.
name
}
</
Select
.
Option
>)
}
</
Select
></
Form
.
Item
>
<
Form
.
Item
name=
"question_style"
label=
"题目类型"
rules=
{
[{
required
:
true
,
message
:
"请选择题目类型"
}]
}
><
Select
placeholder=
"请选择题目类型"
>
{
styles
.
map
((
name
,
index
)
=>
<
Select
.
Option
key=
{
index
+
1
}
value=
{
index
+
1
}
>
{
name
}
</
Select
.
Option
>)
}
</
Select
></
Form
.
Item
>
<
Form
.
Item
name=
"type_id"
label=
"题目分类"
rules=
{
[{
required
:
true
,
message
:
"请选择题目分类"
}]
}
>
{
addingType
?
<
Space
><
Input
value=
{
typeName
}
onChange=
{
(
e
)
=>
setTypeName
(
e
.
target
.
value
)
}
placeholder=
"请输入题目分类"
/><
Button
type=
"link"
onClick=
{
addType
}
>
添加
</
Button
><
Button
type=
"link"
danger
onClick=
{
()
=>
setAddingType
(
false
)
}
>
取消
</
Button
></
Space
>
:
<
Space
.
Compact
><
Select
style=
{
{
minWidth
:
220
}
}
placeholder=
"请选择题目分类"
>
{
types
.
map
((
type
)
=>
<
Select
.
Option
key=
{
type
.
id
}
value=
{
type
.
id
}
>
{
type
.
typename
}
</
Select
.
Option
>)
}
</
Select
><
Button
type=
"link"
onClick=
{
()
=>
setAddingType
(
true
)
}
>
添加分类
</
Button
></
Space
.
Compact
>
}
</
Form
.
Item
>
<
Form
.
Item
name=
"titles"
label=
"题干名称"
rules=
{
[{
required
:
true
,
message
:
"请输入题干名称"
}]
}
extra=
{
questionStyle
===
4
?
"请在需要填空的位置使用2个或以上的下划线进行区分!"
:
""
}
><
Input
.
TextArea
autoSize=
{
{
minRows
:
4
,
maxRows
:
8
}
}
placeholder=
"请输入题干名称"
/></
Form
.
Item
>
<
Form
.
Item
name=
"analysis"
label=
"题目解析"
><
Input
.
TextArea
autoSize=
{
{
minRows
:
3
,
maxRows
:
6
}
}
placeholder=
"请输入题目解析"
/></
Form
.
Item
>
{
needsOptions
&&
<
Form
.
List
name=
"question_list"
>
{
(
fields
,
{
add
,
remove
})
=>
<
Form
.
Item
label=
"题目选项"
>
{
fields
.
map
((
field
)
=>
<
Space
key=
{
field
.
key
}
align=
"start"
style=
{
{
display
:
"flex"
,
marginBottom
:
8
}
}
><
Form
.
Item
{
...
field
}
name=
{
[
field
.
name
,
"option"
]
}
rules=
{
[{
required
:
true
,
message
:
"请输入内容"
}]
}
><
Input
.
TextArea
placeholder=
"请输入选项"
/></
Form
.
Item
><
Form
.
Item
{
...
field
}
name=
{
[
field
.
name
,
"correct"
]
}
valuePropName=
"checked"
><
Checkbox
>
正确答案
</
Checkbox
></
Form
.
Item
><
Button
icon=
{
<
MinusOutlined
/>
}
onClick=
{
()
=>
remove
(
field
.
name
)
}
/></
Space
>)
}{
(
questionStyle
!==
3
||
fields
.
length
<
2
)
&&
<
Button
type=
"dashed"
icon=
{
<
PlusOutlined
/>
}
onClick=
{
()
=>
add
({
option
:
""
,
correct
:
false
})
}
/>
}
</
Form
.
Item
>
}
</
Form
.
List
>
}
{
[
4
,
5
].
includes
(
questionStyle
)
&&
<
Form
.
Item
name=
"answers"
label=
"正确答案"
><
Input
.
TextArea
autoSize=
{
{
minRows
:
4
,
maxRows
:
8
}
}
placeholder=
"请输入正确答案"
/></
Form
.
Item
>
}
<
Form
.
Item
wrapperCol=
{
{
offset
:
5
}
}
><
Space
><
Button
onClick=
{
onClose
}
>
取消
</
Button
><
Button
type=
"primary"
htmlType=
"submit"
>
提交
</
Button
></
Space
></
Form
.
Item
>
</
Form
>
</
Drawer
>;
}
apps/admin/src/modules/books/question-bank/query.ts
0 → 100644
浏览文件 @
8d6aaa09
import
type
{
Dayjs
}
from
"dayjs"
;
export
interface
QuestionBankFilters
{
book_id
?:
string
|
number
;
titles
?:
string
;
type_id
?:
string
|
number
;
question_style
?:
number
;
time
?:
[
Dayjs
|
null
,
Dayjs
|
null
]
|
null
;
}
export
interface
QuestionBankQuery
{
page
:
number
;
page_size
:
number
;
book_id
?:
string
|
number
;
titles
?:
string
;
type_id
?:
string
|
number
;
question_style
?:
number
;
start_time
?:
number
;
end_time
?:
number
;
}
export
function
buildQuestionBankQuery
(
page
:
number
,
pageSize
:
number
,
filters
:
QuestionBankFilters
):
QuestionBankQuery
{
const
query
:
QuestionBankQuery
=
{
page
,
page_size
:
pageSize
};
([
"book_id"
,
"titles"
,
"type_id"
,
"question_style"
]
as
const
).
forEach
((
key
)
=>
{
if
(
filters
[
key
])
query
[
key
]
=
filters
[
key
]
as
never
;
});
if
(
filters
.
time
?.[
0
]
&&
filters
.
time
[
1
])
{
query
.
start_time
=
filters
.
time
[
0
].
startOf
(
"day"
).
unix
();
query
.
end_time
=
filters
.
time
[
1
].
endOf
(
"day"
).
unix
();
}
return
query
;
}
apps/admin/src/modules/books/question-bank/routes.tsx
0 → 100644
浏览文件 @
8d6aaa09
import
{
Navigate
,
useRoutes
}
from
"react-router"
;
import
QuestionBankView
from
"./views/QuestionBankView"
;
export
default
function
QuestionBankRoutes
()
{
return
useRoutes
([{
index
:
true
,
element
:
<
QuestionBankView
/>
},
{
path
:
"*"
,
element
:
<
Navigate
to=
"."
replace
/>
}]);
}
apps/admin/src/modules/books/question-bank/useQuestionBankList.ts
0 → 100644
浏览文件 @
8d6aaa09
import
{
useCallback
,
useEffect
,
useRef
,
useState
}
from
"react"
;
import
{
getQuestionList
}
from
"./api"
;
import
{
buildQuestionBankQuery
,
type
QuestionBankFilters
,
type
QuestionBankQuery
}
from
"./query"
;
export
function
useQuestionBankList
()
{
const
[
filters
,
setFilters
]
=
useState
<
QuestionBankFilters
>
({});
const
[
applied
,
setApplied
]
=
useState
<
QuestionBankFilters
>
({});
const
[
page
,
setPage
]
=
useState
(
1
);
const
[
pageSize
,
setPageSize
]
=
useState
(
10
);
const
[
rows
,
setRows
]
=
useState
<
any
[]
>
([]);
const
[
total
,
setTotal
]
=
useState
(
0
);
const
[
loading
,
setLoading
]
=
useState
(
true
);
const
[
error
,
setError
]
=
useState
<
Error
|
null
>
(
null
);
const
last
=
useRef
<
QuestionBankQuery
>
(
buildQuestionBankQuery
(
1
,
10
,
{}));
const
load
=
useCallback
(
async
(
query
:
QuestionBankQuery
)
=>
{
last
.
current
=
query
;
setLoading
(
true
);
setError
(
null
);
try
{
const
result
:
any
=
await
getQuestionList
(
query
);
if
(
!
result
)
throw
new
Error
(
"请求失败"
);
setRows
(
result
.
list
||
[]);
setTotal
(
result
.
total
||
0
);
}
catch
(
e
)
{
setError
(
e
instanceof
Error
?
e
:
new
Error
(
"请求失败"
));
}
finally
{
setLoading
(
false
);
}
},
[]);
useEffect
(()
=>
{
void
load
(
last
.
current
);
},
[
load
]);
const
run
=
(
p
:
number
,
s
:
number
,
f
:
QuestionBankFilters
)
=>
void
load
(
buildQuestionBankQuery
(
p
,
s
,
f
));
const
search
=
()
=>
{
setApplied
(
filters
);
setPage
(
1
);
run
(
1
,
pageSize
,
filters
);
};
return
{
filters
,
setFilters
,
page
,
pageSize
,
rows
,
total
,
loading
,
error
,
search
,
reset
:
()
=>
{
setFilters
({});
setApplied
({});
setPage
(
1
);
run
(
1
,
pageSize
,
{});
},
refresh
:
()
=>
{
setApplied
(
filters
);
run
(
page
,
pageSize
,
filters
);
},
retry
:
()
=>
void
load
(
last
.
current
),
reloadFirst
:
search
,
changePage
:
(
p
:
number
)
=>
{
setPage
(
p
);
run
(
p
,
pageSize
,
applied
);
},
changePageSize
:
(
s
:
number
)
=>
{
setPage
(
1
);
setPageSize
(
s
);
run
(
1
,
s
,
applied
);
},
};
}
apps/admin/src/modules/books/question-bank/views/QuestionBankView.tsx
0 → 100644
浏览文件 @
8d6aaa09
import
{
App
,
Button
,
DatePicker
,
Drawer
,
Form
,
Input
,
Popover
,
Result
,
Select
,
Space
,
Upload
}
from
"antd"
;
import
type
{
ColumnsType
}
from
"antd/es/table"
;
import
{
useEffect
,
useState
}
from
"react"
;
import
{
useSelector
}
from
"react-redux"
;
import
{
AppList
,
type
AppListFilterItem
}
from
"@/shared/components/AppList"
;
import
{
downloadFile
}
from
"@/utils/common"
;
import
{
deleteQuestion
,
getBookNames
,
getQuestion
,
getQuestionTypes
,
importQuestions
}
from
"../api"
;
import
type
{
QuestionBankFilters
}
from
"../query"
;
import
{
useQuestionBankList
}
from
"../useQuestionBankList"
;
import
QuestionForm
from
"../components/QuestionForm"
;
const
{
RangePicker
}
=
DatePicker
;
const
questionStyles
=
[
"-"
,
"单选题"
,
"多选题"
,
"判断题"
,
"填空题"
,
"简答题"
];
export
default
function
QuestionBankView
()
{
const
list
=
useQuestionBankList
();
const
{
modal
}
=
App
.
useApp
();
const
{
operationPermissionsList
=
[]
}
=
useSelector
((
state
:
any
)
=>
state
.
user
);
const
[
types
,
setTypes
]
=
useState
<
any
[]
>
([]);
const
[
books
,
setBooks
]
=
useState
<
any
[]
>
([]);
const
[
formOpen
,
setFormOpen
]
=
useState
(
false
);
const
[
question
,
setQuestion
]
=
useState
<
any
>
();
const
[
importOpen
,
setImportOpen
]
=
useState
(
false
);
const
[
file
,
setFile
]
=
useState
<
File
>
();
const
[
importResult
,
setImportResult
]
=
useState
<
any
>
();
const
loadTypes
=
async
()
=>
{
const
value
:
any
=
await
getQuestionTypes
();
if
(
value
)
setTypes
(
value
.
list
||
[]);
};
useEffect
(()
=>
{
void
loadTypes
();
void
getBookNames
().
then
((
value
:
any
)
=>
value
&&
setBooks
(
value
));
},
[]);
const
filters
:
AppListFilterItem
<
QuestionBankFilters
>
[]
=
[
{
name
:
"book_id"
,
label
:
"书籍名称"
,
render
:
(
f
)
=>
<
Select
allowClear
showSearch
optionFilterProp=
"children"
value=
{
f
.
value
}
onChange=
{
f
.
onChange
}
style=
{
{
width
:
200
}
}
placeholder=
"请输入书籍名称"
>
{
books
.
map
((
book
)
=>
<
Select
.
Option
key=
{
book
.
book_id
}
value=
{
book
.
book_id
}
>
{
book
.
name
}
</
Select
.
Option
>)
}
</
Select
>
},
{
name
:
"titles"
,
label
:
"题干名称"
,
render
:
(
f
)
=>
<
Input
allowClear
value=
{
f
.
value
||
""
}
onChange=
{
(
e
)
=>
f
.
onChange
(
e
.
target
.
value
)
}
placeholder=
"请输入题干名称"
/>
},
{
name
:
"type_id"
,
label
:
"题目分类"
,
render
:
(
f
)
=>
<
Select
allowClear
value=
{
f
.
value
}
onChange=
{
f
.
onChange
}
style=
{
{
width
:
180
}
}
placeholder=
"请选择题目分类"
>
{
types
.
map
((
type
)
=>
<
Select
.
Option
key=
{
type
.
id
}
value=
{
type
.
id
}
>
{
type
.
typename
}
</
Select
.
Option
>)
}
</
Select
>
},
{
name
:
"question_style"
,
label
:
"题目类型"
,
render
:
(
f
)
=>
<
Select
allowClear
value=
{
f
.
value
}
onChange=
{
f
.
onChange
}
style=
{
{
width
:
160
}
}
placeholder=
"请选择题目类型"
>
{
questionStyles
.
slice
(
1
).
map
((
name
,
index
)
=>
<
Select
.
Option
key=
{
index
+
1
}
value=
{
index
+
1
}
>
{
name
.
replace
(
"题"
,
""
)
}
</
Select
.
Option
>)
}
</
Select
>
},
{
name
:
"time"
,
label
:
"创建时间"
,
render
:
(
f
)
=>
<
RangePicker
value=
{
f
.
value
}
onChange=
{
(
value
)
=>
f
.
onChange
(
value
)
}
/>
},
];
const
edit
=
async
(
id
:
number
)
=>
{
const
value
=
await
getQuestion
(
id
);
if
(
value
)
{
setQuestion
(
value
);
setFormOpen
(
true
);
}
};
const
columns
:
ColumnsType
<
any
>
=
[
{
title
:
"ID"
,
dataIndex
:
"id"
,
align
:
"center"
},
...[
"书籍名称:book_name"
,
"题目名称:titles"
].
map
((
text
)
=>
{
const
[
title
,
dataIndex
]
=
text
.
split
(
":"
);
return
{
title
,
dataIndex
,
align
:
"center"
as
const
,
width
:
250
,
render
:
(
html
:
string
)
=>
<
Popover
overlayStyle=
{
{
maxWidth
:
500
}
}
content=
{
<
div
dangerouslySetInnerHTML=
{
{
__html
:
html
}
}
/>
}
><
div
style=
{
{
display
:
"-webkit-box"
,
WebkitLineClamp
:
3
,
WebkitBoxOrient
:
"vertical"
,
overflow
:
"hidden"
}
}
dangerouslySetInnerHTML=
{
{
__html
:
html
}
}
/></
Popover
>
};
}),
{
title
:
"题目分类"
,
dataIndex
:
"type_id"
,
align
:
"center"
,
render
:
(
id
)
=>
types
.
find
((
type
)
=>
type
.
id
===
id
)?.
typename
},
{
title
:
"题目类型"
,
dataIndex
:
"question_style"
,
align
:
"center"
,
render
:
(
style
)
=>
questionStyles
[
style
]
||
"-"
},
{
title
:
"创建时间"
,
dataIndex
:
"create_time"
,
align
:
"center"
},
{
title
:
"更新时间"
,
dataIndex
:
"update_time"
,
align
:
"center"
},
{
title
:
"操作"
,
render
:
(
_
,
row
)
=>
<
Space
>
{
operationPermissionsList
.
includes
(
"/books/question-bank/getInfoById"
)
&&
<
Button
onClick=
{
()
=>
edit
(
row
.
id
)
}
>
编辑
</
Button
>
}{
operationPermissionsList
.
includes
(
"/books/question-bank/del"
)
&&
<
Button
onClick=
{
()
=>
modal
.
confirm
({
title
:
"提示"
,
content
:
"题目将永久删除,是否继续?"
,
onOk
:
async
()
=>
{
if
(
await
deleteQuestion
(
row
.
id
))
list
.
refresh
();
}
})
}
>
删除
</
Button
>
}
</
Space
>
},
];
const
upload
=
async
()
=>
{
if
(
!
file
)
return
;
const
result
=
await
importQuestions
(
file
);
if
(
result
)
setImportResult
(
result
);
};
const
closeImport
=
()
=>
{
setImportOpen
(
false
);
setFile
(
undefined
);
setImportResult
(
undefined
);
};
return
<
div
className=
"question-bank"
><
AppList
rowKey=
"id"
columns=
{
columns
}
data=
{
{
rows
:
list
.
rows
,
total
:
list
.
total
,
loading
:
list
.
loading
,
error
:
list
.
error
}
}
filters=
{
{
value
:
list
.
filters
,
items
:
filters
,
onChange
:
list
.
setFilters
,
onSearch
:
list
.
search
,
onReset
:
list
.
reset
,
onRefresh
:
list
.
refresh
}
}
pagination=
{
{
page
:
list
.
page
,
pageSize
:
list
.
pageSize
,
pageSizeOptions
:
[
10
,
20
,
30
,
40
],
onChange
:
list
.
changePage
,
onPageSizeChange
:
list
.
changePageSize
}
}
toolbar=
{
<
Space
>
{
operationPermissionsList
.
includes
(
"/books/question-bank/add"
)
&&
<
Button
type=
"primary"
ghost
onClick=
{
()
=>
{
setQuestion
(
undefined
);
setFormOpen
(
true
);
}
}
>
添加
</
Button
>
}{
operationPermissionsList
.
includes
(
"/books/question-bank/import"
)
&&
<
Button
type=
"primary"
ghost
onClick=
{
()
=>
setImportOpen
(
true
)
}
>
导入
</
Button
>
}
</
Space
>
}
onRetry=
{
list
.
retry
}
/>
<
QuestionForm
open=
{
formOpen
}
onClose=
{
()
=>
setFormOpen
(
false
)
}
question=
{
question
}
types=
{
types
}
books=
{
books
}
onTypesChange=
{
loadTypes
}
onSaved=
{
list
.
reloadFirst
}
/>
<
Drawer
open=
{
importOpen
}
mask=
{
false
}
footer=
{
null
}
onClose=
{
closeImport
}
>
{
importResult
?
<
Result
status=
{
importResult
.
data
?.
err
===
0
?
"success"
:
"error"
}
subTitle=
{
importResult
.
msg
}
extra=
{
importResult
.
data
?.
err
!==
0
?
<
Button
type=
"primary"
ghost
onClick=
{
()
=>
downloadFile
(
`temp/${importResult.data?.name}`
)
}
>
下载错误文件
</
Button
>
:
null
}
/>
:
<
Form
onFinish=
{
upload
}
><
Form
.
Item
label=
"下载模板"
><
Button
type=
"primary"
ghost
onClick=
{
()
=>
downloadFile
(
"题库导入示例.xlsx"
)
}
>
下载模板
</
Button
></
Form
.
Item
><
p
>
1.请勿修改模板表头,防止导入失败;2.按照示例填写
</
p
><
Form
.
Item
label=
"导入模板"
required
><
Upload
accept=
".xlsx"
showUploadList=
{
false
}
beforeUpload=
{
(
value
)
=>
{
if
(
!
value
.
name
.
endsWith
(
".xlsx"
))
return
Upload
.
LIST_IGNORE
;
setFile
(
value
);
return
false
;
}
}
><
Button
type=
"primary"
ghost
>
选择文件
</
Button
></
Upload
><
p
>
{
file
?.
name
}
</
p
></
Form
.
Item
><
Space
><
Button
onClick=
{
closeImport
}
>
取消
</
Button
><
Button
type=
"primary"
htmlType=
"submit"
disabled=
{
!
file
}
>
确认
</
Button
></
Space
></
Form
>
}
</
Drawer
>
</
div
>;
}
apps/admin/src/modules/books/routes.tsx
浏览文件 @
8d6aaa09
...
...
@@ -17,7 +17,7 @@ const SaleTable = lazy(() => import("@/pages/books/sale/table"));
const
SaleEdit
=
lazy
(()
=>
import
(
"@/pages/books/sale/edit"
));
const
DiscussList
=
lazy
(()
=>
import
(
"@/pages/books/sale/discussList/index"
));
const
DiscussDetail
=
lazy
(()
=>
import
(
"@/pages/books/sale/discussDetail/index"
));
const
QuestionBank
=
lazy
(()
=>
import
(
"@/pages/books/question-bank/index
"
));
const
QuestionBank
Routes
=
lazy
(()
=>
import
(
"./question-bank/routes
"
));
const
DiscussionRoutes
=
lazy
(()
=>
import
(
"./discussion/routes"
));
const
OrderManagementLayout
=
lazy
(()
=>
import
(
"@/pages/books/order-management/index"
));
const
OrderTable
=
lazy
(()
=>
import
(
"@/pages/books/order-management/tab/table"
));
...
...
@@ -62,7 +62,7 @@ export default function BooksRoutes() {
{
path
:
"discuss-detail"
,
Component
:
DiscussDetail
},
],
},
{
path
:
"question-bank
"
,
Component
:
QuestionBank
},
{
path
:
"question-bank
/*"
,
Component
:
QuestionBankRoutes
},
{
path
:
"discussion/*"
,
Component
:
DiscussionRoutes
},
{
path
:
"order-management"
,
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论