mirror of
https://github.com/fumiama/paper-manager.git
synced 2026-06-21 19:13:22 +08:00
finish 试卷生成
This commit is contained in:
@@ -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' },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 则匹配
|
||||
}
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user