123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696 |
- import {
- DataTableColumn,
- FormInst,
- FormItemRule,
- FormRules,
- NButton,
- NCascader,
- NDataTable,
- NDrawer,
- NDrawerContent,
- NForm,
- NFormItem,
- NIcon,
- NInput,
- NInputNumber,
- NModal,
- NPageHeader,
- NSelect,
- NSpace,
- NSpin,
- NTabPane,
- NTabs,
- useDialog,
- useMessage
- } from 'naive-ui'
- import { computed, defineComponent, onMounted, reactive, ref, watch } from 'vue'
- import { useTabsViewStore } from '@/store/modules/tabsView'
- import { useRoute, useRouter } from 'vue-router'
- import { getLessonType, lessonType } from '@/views/knowledge-manage/knowledgeTypeData'
- import AddQuestionList from '../model/addQuestionList'
- import { difficultyCoefficients, questionTypeCode } from '../question-bank'
- import styles from '../index.module.less'
- import {
- examinationKnowledgePointCategoryPage,
- unitExaminationDetail,
- unitExaminationRandomSave,
- unitExaminationSave
- } from '../../api'
- import { useUserStore } from '@/store/modules/user'
- import { DeleteColumnOutlined, DeleteFilled } from '@vicons/antd'
- import PreviewUnit from './previewUnit'
- import { filterPointCategory } from '..'
- type IUnitype = 'normal' | 'random'
- interface IQuestion {
- /**分值 */
- score: null | any
- /**题目类型多个逗号分割 */
- questionTypeCodes: string
- /**难度,可用值:ONE,TWO,THREE */
- difficultyCoefficient: string
- /** 条件名称(例如考点一) */
- name: string
- /**题目数量 */
- questionNum: null | any
- /** */
- maxScore: number
- /**考点编号 */
- categoryId: null | any
- /**课程类型 */
- courseTypeCode: string
- }
- function createUnitItem(): IQuestion {
- return {
- score: null, //分值
- questionTypeCodes: '', //题目类型多个逗号分割
- difficultyCoefficient: '', // 难度,可用值:ONE,TWO,THREE
- name: '', // 条件名称(例如考点一)
- questionNum: null, //题目数量
- maxScore: 0,
- categoryId: null, // 考点编号
- courseTypeCode: '' // 课程类型
- }
- }
- export default defineComponent({
- name: 'unit-test-index-editAndUpdate',
- emits: ['handleSuccess'],
- setup(props, { emit }) {
- const dialog = useDialog()
- const route = useRoute()
- const query = route.query
- const router = useRouter()
- /**题目类型 */
- const questions = Object.entries(questionTypeCode).map(([value, label]) => ({ label, value }))
- const tabsViewStore = useTabsViewStore()
- const gotoBack = () => {
- tabsViewStore.closeCurrentTab(route)
- router.push({
- path: '/educationalManage/unitExamination'
- })
- }
- /**获取考点类型 */
- const categorys = ref<any[]>([])
- const getType = async () => {
- try {
- const res: any = await examinationKnowledgePointCategoryPage({
- page: 1,
- rows: 1000
- })
- if (Array.isArray(res?.data?.rows)) {
- categorys.value = filterPointCategory(res.data.rows, 'children')
- }
- } catch (error) {}
- }
- const message = useMessage()
- const loading = ref(false)
- const formRef = ref<FormInst | null>(null)
- const userStore = useUserStore()
- const unitType = ref<IUnitype>('normal')
- const saveModel = reactive({
- operatorId: (userStore.getUserInfo as any)?.id || '',
- id: query.unitExaminationId || '',
- name: query.name || '', //测验名称
- courseTypeCode: query.courseTypeCode || (null as any), // 课程类型
- passScore: Number(query.passScore) || (null as any), // 达标分数
- timeMinutes: Number(query.timeMinutes) || (null as any) // 测验时长(分钟
- })
- const pointRef = ref<FormInst | null>()
- const pointList = reactive({
- questionList: [createUnitItem()] as IQuestion[]
- })
- const questionListKey = 'questionList'
- const setPoint = () => {
- const questionListStr = localStorage.getItem(questionListKey) || ''
- try {
- pointList.questionList = JSON.parse(questionListStr)
- } catch (error) {}
- }
- setPoint()
- watch(pointList, () => {
- localStorage.setItem(questionListKey, JSON.stringify(pointList.questionList))
- })
- const rules: FormRules = {
- name: [{ required: true, message: '请填写类型名称', trigger: ['input', 'blur'] }],
- courseTypeCode: [{ required: true, message: '请选择课程类型', trigger: 'change' }],
- passScore: [
- { type: 'number', required: true, message: '请填写达标分数', trigger: ['input', 'blur'] }
- ],
- timeMinutes: [
- { type: 'number', required: true, message: '请填写测验时长', trigger: ['input', 'blur'] }
- ]
- }
- // 获取详情
- const getDetail = async () => {
- if (!saveModel.id) return
- loading.value = true
- try {
- const res: any = await unitExaminationDetail(saveModel.id)
- if (Array.isArray(res?.data)) {
- res.data.forEach((n: any) => {
- let item = {
- ...n?.question,
- answers: n?.answers || []
- }
- // console.log('🚀 ~ item', item)
- modalData.selectList.push(item)
- })
- }
- } catch (error) {}
- loading.value = false
- }
- onMounted(() => {
- getType()
- getDetail()
- })
- const submit = () => {
- // console.log(saveModel)
- formRef.value?.validate(async (err) => {
- if (!err) {
- if (!modalData.selectList.length) {
- message.error('请添加题目')
- return
- }
- if (saveModel.passScore > totalScore.value) {
- message.error('阶段自测的合格分数高于题目的总分值')
- return
- }
- const params: any = {
- ...saveModel,
- questionList: modalData.selectList
- }
- let res: any = null
- console.log(params)
- if (saveModel.id) {
- res = await unitExaminationSave(params)
- } else {
- res = await unitExaminationSave(params)
- }
- if (res?.code == 200) {
- message.success('保存成功')
- gotoBack()
- // emit('handleSuccess')
- } else {
- message.warning('保存失败')
- }
- }
- })
- }
- const modalData = reactive({
- open: false,
- selectList: [] as any[],
- checkList: [] as any[],
- previewOpen: false
- })
- /**已选题目的总分数 */
- const totalScore = computed(() => {
- return modalData.selectList.reduce((total: Number, n: any) => total + n.totalScore, 0)
- })
- /**
- * 删除随机考点
- */
- const handleDeleteItem = (index: number) => {
- // if (pointList.questionList.length === 1) return message.error('最少保留一个考点')
- pointList.questionList.splice(index, 1)
- }
- const columns = (): DataTableColumn[] => {
- return [
- {
- type: 'selection'
- },
- {
- title: '题目名称',
- key: 'name',
- width: 230,
- render(row: any) {
- return (
- <div>
- <div>{row.name}</div>
- </div>
- )
- }
- },
- {
- title: '题目类型',
- key: 'questionTypeCode',
- render(row: any) {
- return questionTypeCode[row.questionTypeCode]
- }
- },
- {
- title: '考点',
- key: 'examinationKnowledgePointCategoryName'
- },
- // {
- // title: '课程类型',
- // key: 'courseTypeCode',
- // render(row: any) {
- // return getLessonType(row.courseTypeCode) || '通用类型'
- // }
- // },
- {
- title: '难度',
- key: 'difficultyCoefficient',
- render(row: any) {
- return (
- difficultyCoefficients.find(
- (n: any) => n.value?.toLocaleUpperCase() === row.difficultyCoefficient
- )?.label || row.difficultyCoefficient
- )
- }
- },
- {
- title: '分值',
- key: 'totalScore'
- },
- {
- title: '操作',
- key: 'action',
- width: 180,
- render(row: any, rowIndex: number) {
- return (
- <NSpace>
- <NButton
- text
- type="primary"
- disabled={rowIndex === 0}
- onClick={() => handleRowMove('up', rowIndex)}
- >
- 上移
- </NButton>
- <NButton
- text
- type="primary"
- disabled={rowIndex === modalData.selectList.length - 1}
- onClick={() => handleRowMove('down', rowIndex)}
- >
- 下移
- </NButton>
- </NSpace>
- )
- }
- }
- ]
- }
- /**删除题目 */
- const handleDeleteQuestion = () => {
- dialog.warning({
- title: '警告',
- content: '是否确认删除选中的题目?',
- positiveText: '确定',
- negativeText: '取消',
- onPositiveClick: async () => {
- modalData.selectList = modalData.selectList.filter(
- (n: any) => !modalData.checkList.includes(n.id)
- )
- }
- })
- }
- /**表格行移动 */
- const handleRowMove = (type: 'up' | 'down', index: number) => {
- if (type === 'up') {
- modalData.selectList[index] = modalData.selectList.splice(
- index - 1,
- 1,
- modalData.selectList[index]
- )[0]
- } else {
- modalData.selectList[index] = modalData.selectList.splice(
- index + 1,
- 1,
- modalData.selectList[index]
- )[0]
- }
- }
- const courseTypeCodeRef = ref()
- /** 随机生成题目 */
- const checkRamdom = () => {
- // courseTypeCodeRef.value.validate()
- // if (!saveModel.courseTypeCode) {
- // message.error('请选择课程类型')
- // return
- // }
- if (!pointList.questionList.length) {
- message.error('请先添加考点')
- return
- }
- if (modalData.selectList.length) {
- dialog.warning({
- title: '警告',
- content: '已有选择的题目,重新生成会清空当前已选的题目,是否继续?',
- positiveText: '继续',
- negativeText: '取消',
- onPositiveClick: async () => {
- createUnitTest()
- }
- })
- return
- }
- createUnitTest()
- }
- const createUnitTest = async () => {
- modalData.selectList = []
- pointRef.value?.validate(async (err) => {
- if (!err) {
- const params: any = pointList.questionList.map((n: any, i: number) => {
- return {
- ...n,
- name: `考点${i + 1}`,
- questionTypeCodes: n.questionTypeCodes?.join(',') || ''
- }
- })
- let res: any = await unitExaminationRandomSave(params)
- if (Array.isArray(res?.data)) {
- modalData.selectList = res.data
- message.success('随机生成题目成功')
- }
- }
- })
- }
- return () => (
- <div class={['system-menu-container', styles['unit-test-index-editAndUpdate']]}>
- <NPageHeader
- on-back={() => gotoBack()}
- title={query.name ? (query.name as any) : '新增阶段自测'}
- ></NPageHeader>
- <div class={['section-container']}>
- <NSpin show={loading.value}>
- <NForm
- ref={formRef}
- model={saveModel}
- rules={rules}
- disabled={query.isLock ? true : false}
- requireMarkPlacement="left"
- labelPlacement="left"
- >
- <NSpace itemStyle={{ width: '30%' }}>
- <NFormItem label="测验名称" path="name" required>
- <NInput
- style={{ width: '210px' }}
- placeholder="请输入测验名称"
- v-model:value={saveModel.name}
- ></NInput>
- </NFormItem>
- <NFormItem ref={courseTypeCodeRef} label="课程类型" path="courseTypeCode" required>
- <NSelect
- style={{ width: '210px' }}
- placeholder="请选择课程类型"
- clearable
- v-model:value={saveModel.courseTypeCode}
- options={lessonType}
- />
- </NFormItem>
- </NSpace>
- <NSpace itemStyle={{ width: '30%' }}>
- <NFormItem label="合格分数" path="passScore" required>
- <NInputNumber
- style={{ width: '210px' }}
- placeholder="请输入合格分数"
- v-model:value={saveModel.passScore}
- clearable
- showButton={false}
- min={0}
- max={100}
- >
- {{
- suffix: () => '分'
- }}
- </NInputNumber>
- </NFormItem>
- <NFormItem label="测验时长" path="timeMinutes" required>
- <NInputNumber
- style={{ width: '210px' }}
- placeholder="请输入测验时长"
- v-model:value={saveModel.timeMinutes}
- clearable
- showButton={false}
- min={0}
- >
- {{
- suffix: () => '分钟'
- }}
- </NInputNumber>
- </NFormItem>
- </NSpace>
- <NFormItem label="组卷条件" required labelPlacement="top">
- <div class={styles.juanWrap} style={{ flex: 1, alignItems: 'center' }}>
- <NForm ref={pointRef} model={pointList} labelPlacement="left">
- {pointList.questionList.map((item: IQuestion, index: number) => {
- return (
- <div class={styles.ramdomItem}>
- <NSpace itemStyle={{ flex: 1 }}>
- <NFormItem
- path={`questionList[${index}].categoryId`}
- required
- rule={{
- required: true,
- message: '请选择考点',
- trigger: ['input', 'blur']
- }}
- >
- {{
- default: () => (
- <NCascader
- v-model:value={item.categoryId}
- class={styles.kaodian}
- options={categorys.value}
- checkStrategy="child"
- childrenField="children"
- valueField="categoryId"
- labelField="name"
- expandTrigger="hover"
- />
- ),
- label: () => (
- <p class={styles.kaoLabel}>
- <span>* </span>考点{index + 1}
- </p>
- )
- }}
- </NFormItem>
- <NFormItem
- label="* 题目数量"
- path={`questionList[${index}].questionNum`}
- rule={{
- type: 'number',
- required: true,
- message: '请输入题目数量',
- trigger: ['input', 'blur']
- }}
- >
- {{
- default: () => (
- <NInputNumber
- style={{ width: '270px' }}
- showButton={false}
- min={0}
- max={100}
- v-model:value={item.questionNum}
- >
- {{
- suffix: () => '题'
- }}
- </NInputNumber>
- ),
- label: () => (
- <p class={styles.kaoLabel}>
- <span>* </span>题目数量
- </p>
- )
- }}
- </NFormItem>
- <NFormItem
- label="难度"
- required
- path={`questionList[${index}].difficultyCoefficient`}
- rule={{
- required: true,
- message: '请选择难度',
- trigger: ['input', 'blur']
- }}
- >
- {{
- default: () => (
- <NSelect
- options={difficultyCoefficients}
- v-model:value={item.difficultyCoefficient}
- />
- ),
- label: () => (
- <p class={styles.kaoLabel}>
- <span>* </span>难度
- </p>
- )
- }}
- </NFormItem>
- <NFormItem
- label="总分值"
- required
- path={`questionList[${index}].score`}
- rule={{
- type: 'number',
- required: true,
- message: '请输入分值',
- trigger: ['input', 'blur']
- }}
- >
- {{
- default: () => (
- <NInputNumber
- showButton={false}
- style={{ width: '270px' }}
- min={0}
- max={100}
- v-model:value={item.score}
- >
- {{
- suffix: () => '分'
- }}
- </NInputNumber>
- ),
- label: () => (
- <p class={styles.kaoLabel}>
- <span>* </span>总分值
- </p>
- )
- }}
- </NFormItem>
- <NFormItem
- label="题型"
- required
- path={`questionList[${index}].questionTypeCodes`}
- rule={{
- type: 'array',
- required: true,
- message: '请选择题目类型',
- trigger: ['input', 'blur']
- }}
- >
- {{
- default: () => (
- <NSelect
- multiple
- clearable
- options={questions}
- v-model:value={item.questionTypeCodes}
- />
- ),
- label: () => (
- <p class={styles.kaoLabel}>
- <span>* </span>题型
- </p>
- )
- }}
- </NFormItem>
- </NSpace>
- <NButton
- class={styles.delIcon}
- size="tiny"
- type="error"
- circle
- onClick={() => handleDeleteItem(index)}
- >
- <NIcon component={<DeleteFilled />}></NIcon>
- </NButton>
- </div>
- )
- })}
- </NForm>
- <NSpace style={{ padding: '10px 10px 0 10px' }}>
- <NButton
- type="warning"
- style={{ width: '170px' }}
- onClick={() => {
- pointList.questionList.push(createUnitItem())
- }}
- >
- + 添加考点
- </NButton>
- <NButton
- type="success"
- onClick={checkRamdom}
- disabled={query.isLock ? true : false}
- >
- {modalData.selectList.length
- ? '根据考点重新生成题目'
- : '根据考点生成测验题目'}
- </NButton>
- <NButton
- type="success"
- onClick={() => (modalData.open = true)}
- disabled={query.isLock ? true : false}
- >
- 手动选择题目添加
- </NButton>
- </NSpace>
- </div>
- </NFormItem>
- </NForm>
- <NSpace vertical wrapItem={false}>
- <NSpace wrapItem={false} justify="space-between">
- <NSpace>
- <div style={{ color: 'red' }}>* </div>已选题目列表
- <div>
- 共 <span style={{ color: 'red' }}>{modalData.selectList.length}</span> 道题目
- </div>
- <div>
- 总分: <span style={{ color: 'red' }}>{totalScore.value}</span> 分
- </div>
- </NSpace>
- <NButton type="error" onClick={handleDeleteQuestion}>
- 批量删除
- </NButton>
- </NSpace>
- <NDataTable
- columns={columns()}
- data={modalData.selectList}
- row-key={(row: any) => row.id}
- onUpdateCheckedRowKeys={(list) => (modalData.checkList = list)}
- ></NDataTable>
- </NSpace>
- <NSpace style={{ paddingTop: '10px' }}>
- {modalData.selectList.length ? (
- <NButton onClick={() => (modalData.previewOpen = true)}>预览题目</NButton>
- ) : null}
- <NButton type="primary" onClick={submit} disabled={query.isLock ? true : false}>
- 保存测验
- </NButton>
- </NSpace>
- </NSpin>
- </div>
- <NModal
- preset="dialog"
- showIcon={false}
- title="选择题目"
- v-model:show={modalData.open}
- style={{ width: '80vw' }}
- >
- <div>
- <AddQuestionList
- selectList={modalData.selectList}
- onSelect={(list: any) => {
- // console.log(row)
- if (list) {
- modalData.selectList = modalData.selectList.concat(list)
- }
- modalData.open = false
- }}
- />
- </div>
- </NModal>
- <NDrawer v-model:show={modalData.previewOpen} width="80vw">
- <NDrawerContent title="预览" closable>
- <PreviewUnit list={modalData.selectList} />
- </NDrawerContent>
- </NDrawer>
- </div>
- )
- }
- })
|