提交 536fa369 authored 作者: 王鹏飞's avatar 王鹏飞

feat: 新增赛项抽签

上级 8ff53d1d
......@@ -23,9 +23,11 @@
"element-plus": "^2.2.32",
"file-saver": "^2.0.5",
"filesize": "^10.0.7",
"html-to-image": "^1.11.11",
"html2pdf.js": "^0.10.1",
"lodash-es": "^4.17.21",
"pinia": "^2.1.4",
"print-js": "^1.6.0",
"ua-parser-js": "^1.0.33",
"universal-cookie": "^4.0.4",
"video.js": "^7.21.1",
......@@ -3768,6 +3770,11 @@
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.4.2.tgz",
"integrity": "sha512-6rOvaUiNKy9lET1X0ECnyZ5O5kSV0PJbtA5yZUgdEF7fGJEVwSLSislltyt7nFwVVALYHQJtfGeAR2Y0A0uJkg=="
},
"node_modules/html-to-image": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz",
"integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
},
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
......@@ -4941,6 +4948,11 @@
"node": ">= 0.8.0"
}
},
"node_modules/print-js": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz",
"integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg=="
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz",
......@@ -9289,6 +9301,11 @@
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.4.2.tgz",
"integrity": "sha512-6rOvaUiNKy9lET1X0ECnyZ5O5kSV0PJbtA5yZUgdEF7fGJEVwSLSislltyt7nFwVVALYHQJtfGeAR2Y0A0uJkg=="
},
"html-to-image": {
"version": "1.11.11",
"resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.11.tgz",
"integrity": "sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA=="
},
"html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
......@@ -10227,6 +10244,11 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
"print-js": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz",
"integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg=="
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmmirror.com/process/-/process-0.11.10.tgz",
......
......@@ -29,9 +29,11 @@
"element-plus": "^2.2.32",
"file-saver": "^2.0.5",
"filesize": "^10.0.7",
"html-to-image": "^1.11.11",
"html2pdf.js": "^0.10.1",
"lodash-es": "^4.17.21",
"pinia": "^2.1.4",
"print-js": "^1.6.0",
"ua-parser-js": "^1.0.33",
"universal-cookie": "^4.0.4",
"video.js": "^7.21.1",
......
<script setup lang="ts">
import printJS from 'print-js'
import { toPng } from 'html-to-image'
interface Props {
data: { competition_name: string; account: string; table_number: string; password: string }
}
withDefaults(defineProps<Props>(), { data: () => ({ competition_name: '', account: '', table_number: '', password: '' }) })
const printRef = ref()
function print() {
toPng(printRef.value)
.then(dataUrl => {
printJS({ printable: dataUrl, type: 'image' })
})
.catch(function (error) {
console.error('oops, something went wrong!', error)
})
}
defineExpose({ print })
</script>
<template>
<div style="width: 0px; height: 0px; overflow: hidden">
<div class="a4" id="printJS-a4" ref="printRef">
<div class="row" v-for="index in 2" :key="index">
<h1 class="title">{{ data.competition_name }}</h1>
<div class="info">
<dl>
<dt>桌台号</dt>
<dd>{{ data.table_number }}</dd>
</dl>
<dl>
<dt>考试账号</dt>
<dd>{{ data.account }}</dd>
</dl>
<dl v-if="data.password">
<dt>考试密码</dt>
<dd>{{ data.password }}</dd>
</dl>
</div>
<div class="sign">
<dl>
<dt>考生签字:</dt>
<dd></dd>
</dl>
<dl>
<dt>裁判签字:</dt>
<dd></dd>
</dl>
</div>
</div>
</div>
</div>
</template>
<style lang="scss">
.a4 {
display: flex;
flex-direction: column;
width: 800px;
height: 1140px;
font-size: 16px;
color: #101010;
// border: 1px solid #000;
.title {
font-size: 20px;
text-align: center;
}
.info {
display: flex;
align-items: center;
justify-content: space-evenly;
dl {
text-align: center;
}
dd {
margin-top: 20px;
font-size: 20px;
}
}
.sign {
display: flex;
align-items: center;
justify-content: space-evenly;
dl {
display: flex;
}
dd {
width: 120px;
border-bottom: 1px solid #000;
}
}
}
.row {
padding: 20px 40px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-evenly;
& + .row {
border-top: 1px dashed #000;
}
}
</style>
......@@ -164,3 +164,32 @@ export function updateExam(data: { id: string; exam_id: string; sort: string })
export function deleteExam(data: { id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-exam/delete', data)
}
// 获取赛项抽签列表
export function getDrawLotList(params: { competition_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/draw-rules', { params })
}
// 添加赛项抽签
export function createDrawLot(data: { competition_id: string; type: string; total: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-draw/create-draw-rule', data)
}
// 更新赛项抽签
export function updateDrawLot(data: { id: string; total: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-draw/update-draw-rule', data)
}
// 删除赛项抽签
export function deleteDrawLot(data: { id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-draw/delete-draw-rule', data)
}
// 获取赛项抽签选手列表
export function getDrawLotStudentList(params: { draw_rule_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/student-draw-list', { params })
}
// 导出赛项抽签选手列表
export function exportDrawLotStudentList(params: { draw_rule_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/export-student-draw-list', { params })
}
// 获取学员的抽签详情
export function getStudentDrawLotInfo(params: { draw_rule_id: string; student_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/student-draw-info', { params })
}
<script setup lang="ts">
import { CirclePlus } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import AppList from '@/components/base/AppList.vue'
import { getDrawLotList, deleteDrawLot } from '../api'
const FormDialog = defineAsyncComponent(() => import('./ViewDrawLotsFormDialog.vue'))
const StudentsDialog = defineAsyncComponent(() => import('./ViewDrawLotsStudentsDialog.vue'))
interface Props {
id: string
}
const props = defineProps<Props>()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
// 列表配置
const listOptions = {
hasPagination: false,
remote: {
httpRequest: getDrawLotList,
params: { competition_id: props.id },
callback(res: any) {
return res?.items ? { list: res.items } : { list: [] }
}
},
columns: [
{ label: '序号', type: 'index', width: 60 },
{
label: '考试类型',
prop: 'type',
computed({ row }: { row: any }) {
const map: Record<string, string> = { '1': '1+X理论考试', '2': '商业数据分析师实操考试' }
return map[row.type] || row.type
}
},
{ label: '总桌数', prop: 'total' },
{ label: '创建人', prop: 'created_operator.real_name' },
{ label: '创建时间', prop: 'created_time' },
{ label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 220 }
]
}
let dialogVisible = $ref(false)
let studentsDialogVisible = $ref(false)
const rowData = ref<any>()
// 新增
function handleAdd() {
rowData.value = undefined
dialogVisible = true
}
// 编辑
function handleUpdate(row: any) {
rowData.value = row
dialogVisible = true
}
// 查阅
function handleView(row: any) {
rowData.value = row
studentsDialogVisible = true
}
// 删除
function handleRemoveClass(row: any) {
ElMessageBox.confirm('确定要删除吗?', '提示').then(() => {
deleteDrawLot({ id: row.id }).then(() => {
ElMessage({ message: '删除成功', type: 'success' })
onUpdateSuccess()
})
})
}
function onUpdateSuccess() {
appList?.refetch()
}
</script>
<template>
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" :icon="CirclePlus" @click="handleAdd">新增</el-button>
</template>
<template #table-x="{ row }">
<el-button link round type="primary">
<router-link :to="{ path: '/drawLots', query: { competition_id: id, id: row.id, type: row.type } }" target="_blank">去抽签</router-link>
</el-button>
<el-button link round type="info" @click="handleView(row)">查看</el-button>
<el-button link round type="primary" @click="handleUpdate(row)">编辑</el-button>
<el-button link round type="danger" @click="handleRemoveClass(row)">删除</el-button>
</template>
</AppList>
<FormDialog v-model="dialogVisible" :data="rowData" @update="onUpdateSuccess" v-if="dialogVisible"></FormDialog>
<StudentsDialog v-model="studentsDialogVisible" :data="rowData" v-if="studentsDialogVisible"></StudentsDialog>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
import type { ContestItem } from '../types'
import { ElMessage } from 'element-plus'
import { pick } from 'lodash-es'
import { createDrawLot, updateDrawLot } from '../api'
interface Props {
data?: any
}
const props = defineProps<Props>()
const emit = defineEmits<{
(e: 'update'): void
(e: 'update:modelValue', visible: boolean): void
}>()
const detail = $ref(inject('detail') as ContestItem)
const formRef = $ref<FormInstance>()
const form = reactive<any>({
competition_id: detail.id,
type: '1',
total: undefined
})
watchEffect(() => {
if (!props.data) return
Object.assign(form, props.data)
})
const rules = ref<FormRules>({
total: [{ required: true, message: '请输入总桌数' }]
})
const isUpdate = $computed(() => {
return !!props.data?.id
})
const title = $computed(() => {
return isUpdate ? '编辑抽签加密' : '新增抽签加密'
})
// 提交
function handleSubmit() {
formRef?.validate().then(() => {
const params = Object.assign({}, pick(form, ['competition_id', 'total', 'type']))
isUpdate ? handleUpdate(params) : handleCreate(params)
})
}
// 新增
function handleCreate(params: any) {
createDrawLot(params).then(() => {
ElMessage({ message: '创建成功', type: 'success' })
emit('update')
emit('update:modelValue', false)
})
}
// 修改
function handleUpdate(params: any) {
params.id = props.data?.id
updateDrawLot(params).then(() => {
ElMessage({ message: '修改成功', type: 'success' })
emit('update')
emit('update:modelValue', false)
})
}
</script>
<template>
<el-dialog :title="title" :close-on-click-modal="false" width="600px" @update:modelValue="$emit('update:modelValue')">
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px">
<el-form-item label="考试类型" prop="type">
<el-radio-group v-model="form.type" :disabled="isUpdate">
<el-radio label="1">1+X理论考试</el-radio>
<el-radio label="2">商业数据分析师实操考试</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="总桌数" prop="total">
<el-input v-model="form.total"></el-input>
</el-form-item>
<el-form-item label="关联赛项">
<el-input :value="detail.name" disabled></el-input>
</el-form-item>
<el-row justify="center">
<el-button type="primary" round auto-insert-space @click="handleSubmit">保存</el-button>
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">取消</el-button>
</el-row>
</el-form>
</el-dialog>
</template>
<script setup lang="ts">
import { getDrawLotStudentList, getStudentDrawLotInfo } from '../api'
import DrawLotsPrint from '@/components/DrawLotsPrint.vue'
interface Props {
data: any
}
const props = defineProps<Props>()
// 列表配置
const listOptions = {
hasPagination: false,
remote: {
httpRequest: getDrawLotStudentList,
params: { draw_rule_id: props.data.id, student_name: '' }
},
filters: [{ type: 'input', prop: 'student_name', label: '选手姓名', placeholder: '请输入选手姓名' }],
columns: [
{ label: '序号', type: 'index', width: 60 },
{ label: '选手姓名', prop: 'student_name' },
{ label: '参赛ID', prop: 'account' },
{ label: '身份证号', prop: 'id_number' },
{ label: '桌号', prop: 'table_number' },
{ label: '更新时间', prop: 'updated_time' },
{ label: '操作', slots: 'table-x', width: 80 }
]
}
const exportUrl = $computed(() => {
return `/api/resource/v1/backend/competition-draw/export-student-draw-list?draw_rule_id=${props.data.id}`
})
const rowData = ref()
const drawLotsPrintRef = ref<InstanceType<typeof DrawLotsPrint> | null>(null)
async function handlePrint(row: any) {
const res = await getStudentDrawLotInfo({ draw_rule_id: props.data.id, student_id: row.student_id })
rowData.value = res.data
nextTick(() => {
drawLotsPrintRef.value?.print()
})
}
</script>
<template>
<el-dialog title="抽签选手列表">
<AppList v-bind="listOptions">
<template #header-buttons>
<a :href="exportUrl" target="_blank">
<el-button type="primary">导出</el-button>
</a>
</template>
<template #table-x="{ row }">
<el-button link round type="primary" @click="handlePrint(row)">打印</el-button>
</template>
</AppList>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
<DrawLotsPrint :data="rowData" ref="drawLotsPrintRef" v-if="rowData"></DrawLotsPrint>
</el-dialog>
</template>
......@@ -9,6 +9,7 @@ const ViewBook = defineAsyncComponent(() => import('../components/ViewBook.vue')
const ViewVideo = defineAsyncComponent(() => import('../components/ViewVideo.vue'))
const ViewQuestion = defineAsyncComponent(() => import('../components/ViewQuestion.vue'))
const ViewExam = defineAsyncComponent(() => import('../components/ViewExam.vue'))
const ViewDrawLots = defineAsyncComponent(() => import('../components/ViewDrawLots.vue'))
const ScoringRulesDialog = defineAsyncComponent(() => import('../components/ScoringRulesDialog.vue'))
const ScoringExpertsDialog = defineAsyncComponent(() => import('../components/ScoringExpertsDialog.vue'))
const ContestantDialog = defineAsyncComponent(() => import('../components/ContestantDialog.vue'))
......@@ -158,10 +159,17 @@ function handleExperts() {
<AppCard title="大赛试卷">
<ViewExam :id="id"></ViewExam>
</AppCard>
<AppCard title="抽签加密">
<ViewDrawLots :id="id"></ViewDrawLots>
</AppCard>
<!-- 评分规则 -->
<ScoringRulesDialog v-model="scoringRulesVisible" :disabled="isStarted" @update="fetchRule" v-if="scoringRulesVisible && detail"></ScoringRulesDialog>
<!-- 评分专家 -->
<ScoringExpertsDialog v-model="scoringExpertsVisible" :disabled="isStarted" @update="fetchExperts" v-if="scoringExpertsVisible && detail"></ScoringExpertsDialog>
<ScoringExpertsDialog
v-model="scoringExpertsVisible"
:disabled="isStarted"
@update="fetchExperts"
v-if="scoringExpertsVisible && detail"></ScoringExpertsDialog>
<!-- 参赛选手 -->
<ContestantDialog v-model="contestantVisible" :disabled="isStarted" v-if="contestantVisible && detail"></ContestantDialog>
<!-- 评分细则 -->
......
import httpRequest from '@/utils/axios'
// 获取赛项抽签规则详情
export function getDrawLot(params: { id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/draw-rule', { params })
}
// 检索赛项抽签学员的信息
export function searchStudent(params: { id_number: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/search-student', { params })
}
// 赛项抽签
export function postDrawLot(data: { draw_rule_id: string; student_id: string }) {
return httpRequest.post('/api/resource/v1/backend/competition-draw/draw', data)
}
// 获取学员的抽签详情
export function getStudentDrawLotInfo(params: { draw_rule_id: string; student_id: string }) {
return httpRequest.get('/api/resource/v1/backend/competition-draw/student-draw-info', { params })
}
import type { RouteRecordRaw } from 'vue-router'
export const routes: Array<RouteRecordRaw> = [
{
path: '/drawLots',
component: () => import('./views/Index.vue')
}
]
<script setup>
import { Search } from '@element-plus/icons-vue'
import DrawLotsPrint from '@/components/DrawLotsPrint.vue'
import { getDrawLot, searchStudent, postDrawLot } from '../api'
const route = useRoute()
const info = ref()
async function fetchInfo() {
const res = await getDrawLot({ id: route.query.id })
info.value = res.data.detail
}
onMounted(() => fetchInfo())
const form = reactive({ id_number: '', competition_id: route.query.competition_id })
const student = ref()
async function search() {
if (!form.id_number) return
const res = await searchStudent(form)
student.value = res.data.detail
}
const dialogVisible = ref(false)
const drawLotInfo = ref()
// 开始抽签
async function handleStart() {
const res = await postDrawLot({ student_id: student.value.id, draw_rule_id: route.query.id })
drawLotInfo.value = res.data
dialogVisible.value = true
}
const drawLotsPrintRef = ref(null)
// 打印
function handlePrint() {
drawLotsPrintRef.value?.print()
}
</script>
<template>
<div class="drawLots">
<div class="inner" v-if="info">
<h1>{{ info.competition.name }}</h1>
<div class="search">
<el-input placeholder="请输入您的身份证号" :suffix-icon="Search" v-model="form.id_number" @keydown.enter="search" />
<el-button type="primary" @click="search">检索数据</el-button>
</div>
<div class="box" v-if="student?.id">
<div class="box-title">确认个人信息</div>
<div class="box-main">
<div class="pic"><img :src="student.picture" v-if="student.picture" /></div>
<div class="box-info">
<div class="name" :title="student.student_name">{{ student.student_name }}</div>
<div class="col">
<dl>
<dt>学校:</dt>
<dd>{{ student.org?.name }}</dd>
</dl>
<dl>
<dt>班级:</dt>
<dd>{{ student.class?.name }}</dd>
</dl>
<dl>
<dt>专业:</dt>
<dd>{{ student.specialty?.name }}</dd>
</dl>
</div>
<div class="col">
<dl>
<dt>身份证号:</dt>
<dd>{{ student.id_number }}</dd>
</dl>
<dl>
<dt>电话:</dt>
<dd>{{ student.mobile }}</dd>
</dl>
</div>
</div>
</div>
</div>
<div class="footer" v-if="student?.id">
<div class="button" @click="handleStart">开始抽签</div>
</div>
</div>
<el-dialog v-model="dialogVisible" class="drawLots-dialog" width="800px">
<div class="bar"></div>
<h2>抽签结果</h2>
<el-form label-suffix=":" label-position="right" label-width="auto">
<el-form-item label="您的桌台号为">
<p>{{ drawLotInfo.table_number }}</p>
</el-form-item>
<el-form-item label="考试账号为">
<p>{{ drawLotInfo.account }}</p>
</el-form-item>
</el-form>
<el-row justify="center">
<el-button type="primary" size="large" @click="handlePrint">打印</el-button>
</el-row>
</el-dialog>
<DrawLotsPrint :data="drawLotInfo" ref="drawLotsPrintRef"></DrawLotsPrint>
</div>
</template>
<style lang="scss" scoped>
.drawLots {
min-height: 100vh;
background: url(@/assets/images/drawLots/bg.png) no-repeat center top;
.inner {
max-width: 1100px;
margin: 0 auto;
}
h1 {
padding: 80px 140px 34px;
font-family: Alibaba PuHuiTi, Alibaba PuHuiTi;
font-weight: bold;
font-size: 48px;
color: #fff;
line-height: 56px;
text-shadow: 0px 3px 6px rgba(0, 0, 0, 0.36);
text-align: center;
}
.search {
display: flex;
align-items: center;
margin: 0 80px 42px;
.el-input {
flex: 1;
height: 60px;
font-size: 20px;
}
.el-button {
margin-left: 20px;
width: 120px;
height: 60px;
font-size: 20px;
}
}
.box {
position: relative;
z-index: 100;
width: 800px;
height: 260px;
background: #ab0a3d;
border-radius: 8px;
border: 3px solid #bf194d;
margin: 0 auto;
.box-title {
margin: 12px 0;
font-size: 22px;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: 400;
color: #ffffff;
text-align: center;
}
.box-main {
width: 760px;
height: 260px;
background: #fff url(@/assets/images/drawLots/bg3.png) no-repeat right 10px center;
border-radius: 8px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-evenly;
box-shadow: 0px 10px 6px 1px rgba(0, 0, 0, 0.16);
.pic {
width: 120px;
height: 140px;
background: #e8eff3 url(@/assets/images/drawLots/avatar.png) no-repeat center center;
border-radius: 8px;
opacity: 1;
border: 2px solid #dfe6ea;
img {
width: 100%;
height: 100%;
}
}
.box-info {
position: relative;
width: 491px;
height: 167px;
background: url(@/assets/images/drawLots/bg2.png) no-repeat;
display: flex;
justify-content: space-between;
.name {
position: absolute;
left: 30px;
top: 10px;
font-size: 16px;
color: #525252;
max-width: 80px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.col {
margin-top: 54px;
margin-right: 20px;
}
dl {
display: flex;
font-size: 16px;
line-height: 1;
dt {
width: 80px;
text-align: right;
color: #a2a2a2;
}
dd {
color: #525252;
}
& + dl {
margin-top: 20px;
}
}
}
}
}
.footer {
margin-top: 22px;
position: relative;
z-index: 1;
height: 170px;
background: url(@/assets/images/drawLots/bg1.png) no-repeat;
overflow: hidden;
.button {
width: 192px;
height: 60px;
margin: 68px auto 0;
background: url(@/assets/images/drawLots/button_bg.png) no-repeat;
background-size: contain;
line-height: 60px;
text-align: center;
font-size: 18px;
color: #fff;
cursor: pointer;
}
}
}
::v-deep(.drawLots-dialog) {
.bar {
position: absolute;
top: 0;
left: -20px;
width: 840px;
height: 30px;
background: #ab0a3d;
border-radius: 8px;
}
.el-dialog__header {
display: none;
}
h2 {
margin: 40px 0;
font-size: 30px;
font-weight: bold;
font-family: Source Han Sans CN, Source Han Sans CN;
color: #525252;
text-align: center;
}
.el-form {
margin-left: 250px;
p {
font-size: 24px;
font-family: Source Han Sans CN, Source Han Sans CN;
font-weight: bold;
color: #ab0a3d;
}
}
.el-button {
width: 180px;
height: 60px;
font-size: 20px;
margin: 40px 0 20px;
}
}
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论