1
0
mirror of https://github.com/fumiama/paper-manager.git synced 2026-06-21 19:13:22 +08:00

finish 试卷生成

This commit is contained in:
源文雨
2023-04-25 00:36:46 +08:00
parent aabd25ea19
commit 940b2618b3
10 changed files with 280 additions and 78 deletions

View File

@@ -1,5 +1,10 @@
import { defHttp, paperHttp } from '/@/utils/http/axios'
import { getFileListModel, AnalyzeFile, FileListGroupItem } from './model/fileListModel'
import {
getFileListModel,
AnalyzeFile,
FileListGroupItem,
GenerateConfig,
} from './model/fileListModel'
import { DownloadFile, FileStatus } from './model/fileModel'
enum Api {
@@ -11,6 +16,8 @@ enum Api {
DlFile = '/dlFile',
GetFileStatus = '/getFileStatus',
GetMajors = '/getMajors',
GenFile = '/genFile',
DlGen = '/dlGen',
}
/**
@@ -81,3 +88,20 @@ export const getFileStatus = (id: number) => {
export const getMajors = () => {
return defHttp.get<string[]>({ url: Api.GetMajors })
}
/**
* @description: Generate File
*/
export const generateFile = (config: GenerateConfig) => {
return defHttp.post<string>({ url: Api.GenFile, params: config }, { errorMessageMode: 'none' })
}
/**
* @description: Download generated file
*/
export const dlGeneratedFile = () => {
return paperHttp.get<any>(
{ url: '/api' + Api.DlGen, responseType: 'blob' },
{ errorMessageMode: 'none' },
)
}

View File

@@ -18,3 +18,11 @@ export interface AnalyzeFile {
code: number
msg: string
}
export interface GenerateConfig {
Distribution: { [x: string]: any } // Distribution is map[majorname]subcount
RateLimit: number // RateLimit 重复率上限
YearStart: number // YearStart 起始年份(空则直到最旧)
YearEnd: number // YearEnd 截止年份(空则直到最新)
TypeMask: number // TypeMask & File.Type != 0 则匹配
}

View File

@@ -95,7 +95,12 @@
async function customSubmitFunc() {
try {
const values = await validate()
emit('next', values)
const data = getDataSource()
if (data.length == 0) {
createMessage.error('必须指定至少一种题型!')
return
}
emit('next', { values, data })
} catch (error) {}
}

View File

@@ -1,11 +1,36 @@
<template>
<div class="step2">
<a-alert message="确认转账后,资金将直接打入对方账户,无法退回。" show-icon />
<a-alert message="确认提交后,将进入下载页面,云端不保存。" show-icon />
<a-descriptions :column="1" class="mt-5">
<a-descriptions-item label="付款账户"> ant-design@alipay.com </a-descriptions-item>
<a-descriptions-item label="收款账户"> test@example.com </a-descriptions-item>
<a-descriptions-item label="收款人姓名"> Vben </a-descriptions-item>
<a-descriptions-item label="转账金额"> 500 </a-descriptions-item>
<a-descriptions-item label="大题数">
{{ state.step1Values.data.length }}
</a-descriptions-item>
<a-descriptions-item label="小题数">
{{
state.step1Values.data
.map((record) => record.count || 0)
.reduce((sum, count) => sum + count, 0)
}}
</a-descriptions-item>
<a-descriptions-item label="重复率上限">
{{ state.step1Values.values.RateLimit[1] / 100 }}
</a-descriptions-item>
<a-descriptions-item label="起止年份" v-if="state.step1Values.values['[YearStart, YearEnd]']">
{{ state.step1Values.values['[YearStart, YearEnd]'][0].$y }} -
{{ state.step1Values.values['[YearStart, YearEnd]'][1].$y }}
</a-descriptions-item>
<a-descriptions-item label="试卷类别" v-if="state.step1Values.values.AB">
{{ state.step1Values.values.AB }}
</a-descriptions-item>
<a-descriptions-item label="考试阶段" v-if="state.step1Values.values.MiddleFinal">
{{ state.step1Values.values.MiddleFinal }}
</a-descriptions-item>
<a-descriptions-item label="考试学期" v-if="state.step1Values.values.FirstSecond">
{{ state.step1Values.values.FirstSecond }}
</a-descriptions-item>
<a-descriptions-item label="考试类型" v-if="state.step1Values.values.OpenClose">
{{ state.step1Values.values.OpenClose }}
</a-descriptions-item>
</a-descriptions>
<a-divider />
<BasicForm @register="register" />
@@ -14,7 +39,9 @@
<script lang="ts">
import { defineComponent } from 'vue'
import { BasicForm, useForm } from '/@/components/Form'
import { step2Schemas } from './data'
import { state } from './data'
import { generateFile } from '/@/api/page'
import { useMessage } from '/@/hooks/web/useMessage'
import { Alert, Divider, Descriptions } from 'ant-design-vue'
export default defineComponent({
@@ -27,11 +54,10 @@
},
emits: ['next', 'prev'],
setup(_, { emit }) {
const [register, { validate, setProps }] = useForm({
const [register, { setProps }] = useForm({
labelWidth: 80,
schemas: step2Schemas,
actionColOptions: {
span: 14,
span: 16,
},
resetButtonOptions: {
text: '上一步',
@@ -43,30 +69,74 @@
submitFunc: customSubmitFunc,
})
const { createMessage } = useMessage()
async function customResetFunc() {
emit('prev')
}
async function customSubmitFunc() {
try {
const values = await validate()
setProps({
submitButtonOptions: {
loading: true,
},
})
setTimeout(() => {
setProps({
submitButtonOptions: {
loading: false,
},
})
emit('next', values)
}, 1500)
} catch (error) {}
let ys = 0
let ye = 0
if (state.step1Values.values['[YearStart, YearEnd]']) {
ys = state.step1Values.values['[YearStart, YearEnd]'][0].$y
ye = state.step1Values.values['[YearStart, YearEnd]'][1].$y
}
let tm = 0
if (state.step1Values.values.AB) {
if (state.step1Values.values.AB == 'A') tm |= 1
else if (state.step1Values.values.AB == 'B') tm |= 2
}
if (state.step1Values.values.MiddleFinal) {
if (state.step1Values.values.MiddleFinal == '中') tm |= 1 << 4
else if (state.step1Values.values.MiddleFinal == '末') tm |= 2 << 4
}
if (state.step1Values.values.FirstSecond) {
if (state.step1Values.values.FirstSecond == '1') tm |= 1 << 8
else if (state.step1Values.values.FirstSecond == '2') tm |= 2 << 8
}
if (state.step1Values.values.OpenClose) {
if (state.step1Values.values.OpenClose == '开卷') tm |= 1 << 12
else if (state.step1Values.values.OpenClose == '一页纸开卷') tm |= 2 << 12
else if (state.step1Values.values.OpenClose == '闭卷') tm |= 4 << 12
}
const data = await generateFile({
Distribution: state.step1Values.data.reduce((acc, { major, count }) => {
console.log(major, count)
acc[major] = count
return acc
}, {}),
RateLimit: state.step1Values.values.RateLimit[1] / 100,
YearStart: ys,
YearEnd: ye,
TypeMask: tm,
})
setProps({
submitButtonOptions: {
loading: false,
},
})
emit('next', data)
} catch (error) {
createMessage.error((error as unknown as Error).message)
setProps({
submitButtonOptions: {
loading: false,
},
})
}
}
return { register }
return {
register,
state,
}
},
})
</script>

View File

@@ -1,36 +1,87 @@
<template>
<div class="step3">
<a-result status="success" title="操作成功" sub-title="预计两小时内到账">
<a-result status="success" title="试卷生成成功" :sub-title="msg">
<template #extra>
<a-button type="primary" @click="redo"> 转一笔 </a-button>
<a-button> 查看账单 </a-button>
<a-button type="primary" @click="redo"> 次生成 </a-button>
<a-button @click="downloadDocx"> 下载试卷 </a-button>
</template>
</a-result>
<div class="desc-wrap">
<a-descriptions :column="1" class="mt-5">
<a-descriptions-item label="付款账户"> ant-design@alipay.com </a-descriptions-item>
<a-descriptions-item label="收款账户"> test@example.com </a-descriptions-item>
<a-descriptions-item label="收款人姓名"> Vben </a-descriptions-item>
<a-descriptions-item label="转账金额"> 500 </a-descriptions-item>
</a-descriptions>
<div class="docxWrap" :style="{ width }">
<div ref="docxRef"></div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { defineComponent, ref } from 'vue'
import { Result, Descriptions } from 'ant-design-vue'
import { renderAsync } from 'docx-preview'
import { state } from './data'
import { dlGeneratedFile } from '/@/api/page'
import { useMessage } from '/@/hooks/web/useMessage'
import { downloadByData } from '/@/utils/file/download'
let docxRef = ref(null)
let docxNameRef = ref('paper.docx')
let docxSizeRef = ref(0)
let docxBlob: Blob | null = null
function loadDocx(file: Blob) {
docxBlob = file
renderAsync(file, docxRef.value as unknown as HTMLElement, undefined, {
className: 'docx', // 默认和文档样式类的类名/前缀
inWrapper: false, // 启用围绕文档内容渲染包装器
ignoreWidth: false, // 禁止页面渲染宽度
ignoreHeight: false, // 禁止页面渲染高度
ignoreFonts: false, // 禁止字体渲染
breakPages: false, // 在分页符上启用分页
ignoreLastRenderedPageBreak: true, //禁用lastRenderedPageBreak元素的分页
experimental: true, // 启用实验性功能(制表符停止计算)
trimXmlDeclaration: true, // 如果为真xml声明将在解析之前从xml文档中删除
debug: false, // 启用额外的日志记录
})
}
function downloadDocx() {
downloadByData(docxBlob as BlobPart, docxNameRef.value)
}
export default defineComponent({
components: {
[Result.name]: Result,
[Descriptions.name]: Descriptions,
[Descriptions.Item.name]: Descriptions.Item,
},
props: {
width: {
type: String as PropType<string>,
default: '100%',
},
},
emits: ['redo'],
setup(_, { emit }) {
const { createMessage } = useMessage()
;(async () => {
try {
const data = await dlGeneratedFile()
if (data) {
loadDocx(data)
return
}
} catch (error) {
createMessage.error('加载docx错误: ' + (error as unknown as Error).message)
}
})()
return {
msg: state.step2Values,
redo: () => {
emit('redo')
},
docxRef,
downloadDocx,
docxNameRef,
docxSizeRef,
}
},
})
@@ -41,9 +92,12 @@
margin: 0 auto;
}
.desc-wrap {
padding: 24px 40px;
margin-top: 24px;
background-color: @background-color-light;
.docxWrap {
padding-top: 0px;
margin: 0 auto;
overflow-x: auto;
display: grid;
align-items: center;
justify-items: center;
}
</style>

View File

@@ -1,4 +1,12 @@
import { FormSchema } from '/@/components/Form'
import { reactive } from 'vue'
export const state = reactive({
initSetp2: false,
initSetp3: false,
step1Values: { values: {} as any, data: [] as Recordable<any>[] },
step2Values: {} as any,
})
export const step1Schemas: FormSchema[] = [
{
@@ -119,16 +127,3 @@ export const step1Schemas: FormSchema[] = [
},
},
]
export const step2Schemas: FormSchema[] = [
{
field: 'pwd',
component: 'InputPassword',
label: '支付密码',
required: true,
defaultValue: '123456',
colProps: {
span: 24,
},
},
]

View File

@@ -1,15 +1,15 @@
<template>
<PageWrapper
title="分步表单"
:title="t('routes.genfile.name')"
contentBackground
content=" 将一个冗长或用户不熟悉的表单任务分成多个步骤,指导用户完成。"
content="使用自定义限制条件生成试卷"
contentClass="p-4"
>
<div class="step-form-form">
<a-steps :current="current">
<a-step title="填写转账信息" />
<a-step title="确认转账信息" />
<a-step title="完成" />
<a-step title="填写信息" />
<a-step title="确认生成" />
<a-step title="下载" />
</a-steps>
</div>
<div class="mt-5">
@@ -25,12 +25,14 @@
</PageWrapper>
</template>
<script lang="ts">
import { defineComponent, ref, reactive, toRefs } from 'vue'
import { defineComponent, ref, toRefs } from 'vue'
import { state } from './data'
import Step1 from './Step1.vue'
import Step2 from './Step2.vue'
import Step3 from './Step3.vue'
import { PageWrapper } from '/@/components/Page'
import { Steps } from 'ant-design-vue'
import { useI18n } from '/@/hooks/web/useI18n'
export default defineComponent({
name: 'FormStepPage',
@@ -45,15 +47,12 @@
setup() {
const current = ref(0)
const state = reactive({
initSetp2: false,
initSetp3: false,
})
const { t } = useI18n()
function handleStep1Next(step1Values: any) {
current.value++
state.initSetp2 = true
console.log(step1Values)
state.step1Values = step1Values
}
function handleStepPrev() {
@@ -63,7 +62,7 @@
function handleStep2Next(step2Values: any) {
current.value++
state.initSetp3 = true
console.log(step2Values)
state.step2Values = step2Values
}
function handleRedo() {
@@ -73,6 +72,7 @@
}
return {
t,
current,
handleStep1Next,
handleStep2Next,