提交 54bb1fc9 authored 作者: 王鹏飞's avatar 王鹏飞

Merge branch 'master' into gdrtvu

......@@ -295,6 +295,13 @@
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true
"whenever": true,
"DirectiveBinding": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"onWatcherCleanup": true,
"useId": true,
"useModel": true,
"useTemplateRef": true
}
}
......@@ -67,6 +67,7 @@ declare global {
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
const provide: typeof import('vue')['provide']
const reactify: typeof import('@vueuse/core')['reactify']
......@@ -175,6 +176,7 @@ declare global {
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
const useGamepad: typeof import('@vueuse/core')['useGamepad']
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
const useId: typeof import('vue')['useId']
const useIdle: typeof import('@vueuse/core')['useIdle']
const useImage: typeof import('@vueuse/core')['useImage']
const useInfiniteScroll: typeof import('@vueuse/core')['useInfiniteScroll']
......@@ -194,6 +196,7 @@ declare global {
const useMemoize: typeof import('@vueuse/core')['useMemoize']
const useMemory: typeof import('@vueuse/core')['useMemory']
const useMin: typeof import('@vueuse/math')['useMin']
const useModel: typeof import('vue')['useModel']
const useMounted: typeof import('@vueuse/core')['useMounted']
const useMouse: typeof import('@vueuse/core')['useMouse']
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
......@@ -243,6 +246,7 @@ declare global {
const useSum: typeof import('@vueuse/math')['useSum']
const useSupported: typeof import('@vueuse/core')['useSupported']
const useSwipe: typeof import('@vueuse/core')['useSwipe']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
const useTextDirection: typeof import('@vueuse/core')['useTextDirection']
const useTextSelection: typeof import('@vueuse/core')['useTextSelection']
......@@ -294,6 +298,6 @@ declare global {
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -39,8 +39,8 @@
"ua-parser-js": "^1.0.33",
"universal-cookie": "^4.0.4",
"video.js": "^7.21.1",
"vue": "^3.4.25",
"vue-router": "^4.3.2",
"vue": "^3.5.12",
"vue-router": "^4.4.5",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
......@@ -53,8 +53,8 @@
"@types/node": "^20.3.1",
"@types/ua-parser-js": "^0.7.36",
"@types/video.js": "^7.3.52",
"@vitejs/plugin-vue": "^5.0.4",
"@vue-macros/reactivity-transform": "^0.4.4",
"@vitejs/plugin-vue": "^5.1.4",
"@vue-macros/reactivity-transform": "^1.1.2",
"@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.5.1",
"chalk": "^5.2.0",
......@@ -62,9 +62,10 @@
"eslint-plugin-vue": "^9.25.0",
"sass": "^1.58.3",
"typescript": "~5.4.5",
"unplugin-auto-import": "^0.17.5",
"vite": "^5.2.10",
"unplugin-auto-import": "^0.17.8",
"vite": "^5.4.9",
"vite-plugin-checker": "^0.6.4",
"vite-plugin-mkcert": "^1.17.6",
"vue-tsc": "^1.8.27"
}
}
......@@ -64,7 +64,7 @@ function handleClick(path: string) {
<div class="logo">
<router-link to="/"><img :src="appConfig.logo" /></router-link>
</div>
<div class="line"></div>
<div class="line" v-if="appConfig.title"></div>
</template>
<h1 class="app-name">
<router-link to="/">{{ appConfig.title }}</router-link>
......
......@@ -46,7 +46,7 @@ const appConfigList = [
},
{
system: 'dml',
title: '数字营销实验室',
title: '数智营销实践教学平台',
logo: 'https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg',
hosts: ['saas-dml-web'],
dmlURL: import.meta.env.VITE_DML_PRO_URL
......@@ -67,12 +67,73 @@ const appConfigList = [
xExamLabel: '商务数据分析理论考试',
labExamLabel: '商务数据分析实操考试',
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL
},
{
system: 'swsjfxs',
title: '商务数据分析师',
logo: 'https://webapp-pub.ezijing.com/website/base/images/logo_swsjfxs.png',
favicon: 'https://webapp-pub.ezijing.com/website/base/images/favicon_swsjfxs.png',
hosts: ['saas-lab-bda'],
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' },
{ name: '大赛成绩查询', path: '/student/contest/score' }
],
xTrainLabel: '理论训练',
labTrainLabel: '实操训练',
xExamLabel: '理论考试',
labExamLabel: '实操考试',
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true // 隐藏大赛工具栏
},
{
system: 'amo',
title: '全媒体运营师',
logo: 'https://webapp-pub.ezijing.com/website/base/images/logo_swsjfxs.png',
favicon: 'https://webapp-pub.ezijing.com/website/base/images/favicon_swsjfxs.png',
hosts: ['saas-lab-amo'],
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' },
{ name: '大赛成绩查询', path: '/student/contest/score' }
],
xTrainLabel: '理论训练',
labTrainLabel: '实操训练',
xExamLabel: '理论考试',
labExamLabel: '实操考试',
loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true // 隐藏大赛工具栏
},
{
system: 'default',
title: '商业数据分析与应用大赛',
logo: 'https://zws-imgs-pub.ezijing.com/pc/base/ezijing-logo.svg',
// logo: 'https://webapp-pub.ezijing.com/website/base/images/logo_swsjfxs.png',
// favicon: 'https://webapp-pub.ezijing.com/website/base/images/favicon_swsjfxs.png',
hosts: ['saas-lab-bda2'],
studentMenus: [
{ name: '首页', path: '/' },
{ name: '我的大赛', path: '/student/contest' }
// { name: '大赛成绩查询', path: '/student/contest/score' }
],
xTrainLabel: '理论训练',
labTrainLabel: '实操训练',
xExamLabel: '理论考试',
labExamLabel: '实操考试',
// loginURL: import.meta.env.VITE_SWSJFXS_LOGIN_URL,
hideAvailableEvents: true, // 隐藏可参与赛项
hidePracticalTestPaper: true, // 隐藏实操试卷
hideContestToolbar: true // 隐藏大赛工具栏
}
]
export function useAppConfig() {
const found = appConfigList.find(item => {
return item.hosts.find(host => location.host.includes(host))
return item.hosts.find(host => location.host.split('.').includes(host))
})
const appConfig = found || appConfigList[0]
......
<script setup lang="ts">
import type { RecordItem, ReportItem } from '../types'
import { getReportList } from '../api'
interface Props {
data: RecordItem
}
const props = defineProps<Props>()
const reportList = ref<ReportItem[]>([])
async function fetchReport() {
const res = await getReportList({ competition_id: props.data.competition_id, student_id: props.data.student_id })
reportList.value = res.data.items
}
onMounted(() => {
fetchReport()
})
</script>
<template>
<el-dialog title="查看报告" width="500px">
<ul>
<li v-for="item in reportList" :key="item.id" style="line-height: 24px">
<a :href="`https://view.officeapps.live.com/op/view.aspx?src=${item.url}`" target="_blank">
{{ item.name }}
<el-button size="small" type="primary" style="margin-left: 10px">查看</el-button>
</a>
</li>
</ul>
<template #footer>
<el-row justify="center">
<el-button round auto-insert-space @click="$emit('update:modelValue', false)">关闭</el-button>
</el-row>
</template>
</el-dialog>
</template>
export interface RecordItem {
check_time: string
checked_count: number
checked_flag_name: string
checked_flag: boolean
checker_id?: string
class_id_name: string
class_id: string
class_name: string
commit_time: string
competition_competitor_pictures: any
competition_id_name: string
competition_id: string
competition_is_more_status: string
competition_rule_type: string
competition_uri: string
course_name?: string
experiment_id: string
experiment_name: string
file?: string
gender: string
grade: string
id_number: string
id: string
login_id: string
need_check_count: number
organ_id_name: string
organ_id: string
pictures?: string
score: string
publish_status_name: string
publish_status: string
score_details?: string
score_status: string
score: string
sno_number: string
specialty_id_name: string
specialty_id: string
specialty_name: string
status: 0 | 1 | 2
status_name: string
status: 0 | 1 | 2
student_id: string
student_name: string
competition_competitor_pictures: any
train_count: string
updated_time: string
}
export interface FileItem {
......
......@@ -8,6 +8,7 @@ const SyncExamDialog = defineAsyncComponent(() => import('../components/SyncExam
const ImportExamDialog = defineAsyncComponent(() => import('../components/ImportExamDialog.vue'))
const ImportScoreDialog = defineAsyncComponent(() => import('../components/ImportScoreDialog.vue'))
const ScoreViewPicturesDialog = defineAsyncComponent(() => import('../components/ScoreViewPicturesDialog.vue'))
const ReportDialog = defineAsyncComponent(() => import('../components/ReportDialog.vue'))
const route = useRoute()
......@@ -129,6 +130,7 @@ function onUpdateSuccess() {
appList?.refetch()
}
// 查看截图
const viewPictureVisible = ref(false)
const rowData = ref()
function handleViewPicture(row: any) {
......@@ -136,6 +138,13 @@ function handleViewPicture(row: any) {
viewPictureVisible.value = true
}
// 查看报告
const viewReportVisible = ref(false)
function handleViewReport(row: any) {
rowData.value = row
viewReportVisible.value = true
}
// function getModuleStatus(row: any, index: number) {
// const [first] = row.student_module_status_list
// try {
......@@ -155,13 +164,28 @@ function handleViewPicture(row: any) {
<AppCard title="大赛评分">
<AppList v-bind="listOptions" ref="appList">
<template #header-buttons>
<el-button type="primary" round :icon="Refresh" @click="syncDialogVisible = true" v-permission="'v1-expert-check-sync-exam'"
<el-button
type="primary"
round
:icon="Refresh"
@click="syncDialogVisible = true"
v-permission="'v1-expert-check-sync-exam'"
>系统同步考试成绩</el-button
>
<el-button type="primary" round :icon="Upload" @click="importExamVisible = true" v-permission="'v1-expert-check-import-exam'"
<el-button
type="primary"
round
:icon="Upload"
@click="importExamVisible = true"
v-permission="'v1-expert-check-import-exam'"
>批量导入考试成绩</el-button
>
<el-button type="primary" round :icon="Upload" @click="importScoreVisible = true" v-permission="'v1-expert-check-import-score'"
<el-button
type="primary"
round
:icon="Upload"
@click="importScoreVisible = true"
v-permission="'v1-expert-check-import-score'"
>批量导入完整评分</el-button
>
</template>
......@@ -170,9 +194,11 @@ function handleViewPicture(row: any) {
<span :class="{ 'is-info': row.score_name !== '--' }">{{ row.score_name }}</span>
</template>
<template #table-x="{ row }">
<!-- <el-button text type="primary" v-if="row.publish_status === '0'" v-permission="'v1-expert-check-set-score'">
<el-button text type="primary" v-if="row.publish_status === '0'" v-permission="'v1-expert-check-set-score'">
<router-link :to="`/admin/contest/check/${row.id}`" target="_blank">评分</router-link>
</el-button> -->
</el-button>
<br />
<el-button text type="primary" @click="handleViewReport(row)">查看报告</el-button><br />
<el-button text type="primary" @click="handleViewPicture(row)">查看截图</el-button>
</template>
</AppList>
......@@ -182,9 +208,14 @@ function handleViewPicture(row: any) {
<!-- 批量导入考试成绩 -->
<ImportExamDialog v-model="importExamVisible" @update="onUpdateSuccess" v-if="importExamVisible"></ImportExamDialog>
<!-- 批量导入完整评分 -->
<ImportScoreDialog v-model="importScoreVisible" @update="onUpdateSuccess" v-if="importScoreVisible"></ImportScoreDialog>
<ImportScoreDialog
v-model="importScoreVisible"
@update="onUpdateSuccess"
v-if="importScoreVisible"></ImportScoreDialog>
<!-- 查看截图 -->
<ScoreViewPicturesDialog v-model="viewPictureVisible" :data="rowData" v-if="rowData"></ScoreViewPicturesDialog>
<!-- 查看报告 -->
<ReportDialog v-model="viewReportVisible" :data="rowData" v-if="rowData && viewReportVisible"></ReportDialog>
</template>
<style lang="scss">
......
......@@ -43,6 +43,7 @@ const formRef = $ref<FormInstance>()
const form = reactive({
id: '',
name: '',
client_id: '',
host_unit_id: '',
organizer_ids: [],
technical_support_unit_id: '',
......@@ -197,6 +198,7 @@ function handleSubmit() {
const params: ContestUpdateParams = pick(mergedForm, [
'id',
'name',
'client_id',
'host_unit_id',
'organizer_ids',
'technical_support_unit_id',
......@@ -244,11 +246,21 @@ function handleDateRangeChange(value: any) {
form.date = secondDate
}
}
const clientList = [
{ label: '商务数据分析师赛项', value: 'business_data_analyst' },
{ label: '全媒体运营师赛项', value: 'all_media_operator' }
]
</script>
<template>
<el-dialog :title="title" :close-on-click-modal="false" align-center width="600px" @update:modelValue="value => $emit('update:modelValue', value)">
<el-form ref="formRef" :model="form" :rules="rules" label-width="110px">
<el-form-item label="客户端标识" prop="client_id">
<el-select v-model="form.client_id" style="width: 100%" clearable>
<el-option v-for="item in clientList" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="赛项名称" prop="name">
<el-input v-model="form.name" :disabled="isUpdate" />
</el-form-item>
......@@ -304,7 +316,8 @@ function handleDateRangeChange(value: any) {
v-for="item in form.train_platform_configs"
:key="item.platform_key">
<div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 180px">{{ item.name }}</span>
<!-- <span style="margin-right: 10px; width: 180px">{{ item.name }}</span> -->
<el-input v-model="item.name" style="margin-right: 10px; width: 200px" />
<el-input v-model="item.url" />
</div>
</el-checkbox>
......@@ -318,7 +331,8 @@ function handleDateRangeChange(value: any) {
v-for="item in form.competition_platform_configs"
:key="item.platform_key">
<div style="display: flex; align-items: center">
<span style="margin-right: 10px; width: 180px">{{ item.name }}</span>
<!-- <span style="margin-right: 10px; width: 180px">{{ item.name }}</span> -->
<el-input v-model="item.name" style="margin-right: 10px; width: 200px" />
<el-input v-model="item.url" />
</div>
</el-checkbox>
......
......@@ -12,6 +12,8 @@ const props = defineProps<Props>()
const appList = $ref<InstanceType<typeof AppList> | null>(null)
const route = useRoute()
// 列表配置
const listOptions = $computed(() => {
return {
......@@ -52,7 +54,7 @@ function onUpdateSuccess() {
<template>
<AppCard title="发布成绩">
<el-descriptions>
<el-descriptions-item label="赛项名称:">{{ $route.query.name }}</el-descriptions-item>
<el-descriptions-item label="赛项名称:">{{ route.query.name }}</el-descriptions-item>
</el-descriptions>
<AppList v-bind="listOptions" ref="appList">
<template #table-count="{ row }"> {{ row.checked_count }}/{{ row.need_check_count }} </template>
......
......@@ -19,7 +19,8 @@ const formRef = ref<FormInstance>()
const form = reactive({
experiment_name: '',
organ_id: '',
sso_id: ''
sso_id: '',
is_copy_class: 0
})
onMounted(() => {
......@@ -72,6 +73,14 @@ async function handleSubmit() {
<el-option v-for="item in teachers" :key="item.id" :label="item.name" :value="item.sso_id"></el-option>
</el-select>
</el-form-item>
<el-form-item label="是否复制班级" prop="experiment_name">
<el-switch
v-model="form.is_copy_class"
active-text="是"
inactive-text="否"
:active-value="1"
:inactive-value="0" />
</el-form-item>
</el-form>
<template #footer>
<el-row justify="center">
......
......@@ -4,3 +4,8 @@ import httpRequest from '@/utils/axios'
export function qwenChat(data: any) {
return httpRequest.post('/api/lab/v1/experiment/qwen/chat', data, { headers: { 'Content-Type': 'application/json' } })
}
// 聊天(流式响应)
export function aiChat(data: any) {
return httpRequest.post('/api/ai/api/chat/data-analysis/generate-chart', data, { headers: { 'Content-Type': 'application/json' } })
}
......@@ -3,7 +3,7 @@ const emit = defineEmits(['success'])
const file = ref()
const onSuccess = res => {
file.value = res.data.detail
file.value = res.data
emit('success', file.value)
}
</script>
......@@ -12,13 +12,13 @@ const onSuccess = res => {
<el-upload
class="ai-upload"
drag
action="/api/lab/v1/experiment/qwen/upload-file"
action="/api/ai/api/upload-file"
accept=".csv, .xls, .xlsx, text/csv, application/csv,text/comma-separated-values, application/csv, application/excel,application/vnd.msexcel, text/anytext, application/vnd. ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
:data="{ purpose: 'file-extract' }"
:show-file-list="false"
:on-success="onSuccess">
<ul class="ai-upload-list" v-if="file">
<li>{{ file.filename }}</li>
<li>{{ file.file_name }}</li>
</ul>
<div class="ai-upload-box">
<img src="@/assets/images/ai_plus.png" height="40" />
......
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { ElMessage } from 'element-plus'
// import { fetchEventSource } from '@microsoft/fetch-event-source'
// import { ElMessage } from 'element-plus'
// export function useChat() {
// const messages = ref([])
// const isLoading = ref(false)
// async function post() {
// isLoading.value = true
// await fetchEventSource('/api/lab/v1/experiment/qwen/chat', {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify({ model: 'qwen-long', messages: messages.value }),
// async onopen(response) {
// if (response.ok) {
// return response
// } else {
// throw response
// }
// },
// onmessage(res) {
// console.log(res.data)
// try {
// const message = JSON.parse(res.data)
// if (message.error) {
// ElMessage.error(message.error.message)
// return
// }
// const id = message.id
// const messageIndex = messages.value.findIndex(session => session.id === id)
// let content = message?.choices[0]?.delta.content || ''
// content = content.replaceAll('\n', '<br/>')
// if (messageIndex === -1) {
// messages.value.push({ id, role: 'assistant', content })
// } else {
// messages.value[messageIndex].content = messages.value[messageIndex].content + content
// }
// isLoading.value = false
// } catch (error) {
// console.log(error)
// isLoading.value = false
// }
// },
// onerror(err) {
// isLoading.value = false
// throw err
// }
// })
// }
// return { messages, post, isLoading }
// }
import { isString } from 'lodash-es'
import { aiChat } from '../api'
export function useChat() {
const messages = ref([])
const isLoading = ref(false)
async function post() {
async function post(data) {
isLoading.value = true
await fetchEventSource('/api/lab/v1/experiment/qwen/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ model: 'qwen-long', messages: messages.value }),
async onopen(response) {
if (response.ok) {
return response
} else {
throw response
}
},
onmessage(res) {
console.log(res.data)
try {
const message = JSON.parse(res.data)
if (message.error) {
ElMessage.error(message.error.message)
return
}
const id = message.id
const messageIndex = messages.value.findIndex(session => session.id === id)
let content = message?.choices[0]?.delta.content || ''
const response = await aiChat(data)
const items = response.data.items || []
items
.filter(item => item.type !== 'flow')
.forEach(item => {
let content = item.content || ''
if (item.type === 'text') {
content = content.replaceAll('\n', '<br/>')
if (messageIndex === -1) {
messages.value.push({ id, role: 'assistant', content })
} else {
messages.value[messageIndex].content = messages.value[messageIndex].content + content
}
isLoading.value = false
if (!isString(content)) {
try {
content = JSON.stringify(content)
} catch (error) {
console.log(error)
isLoading.value = false
}
},
onerror(err) {
isLoading.value = false
throw err
}
console.log(content)
messages.value.push({ role: 'assistant', content, type: item.type })
})
} catch (error) {
isLoading.value = false
throw error
} finally {
isLoading.value = false
}
}
return { messages, post, isLoading }
}
......@@ -5,16 +5,16 @@ const { messages, post, isLoading } = useChat()
const chatInput = ref('')
const file = ref(null)
const onUploadSuccess = res => {
const message = { role: 'system', content: `fileid://${res.id}` }
messages.value.push(message)
file.value = res
}
async function postMessage() {
if (!chatInput.value) return
const message = { role: 'user', content: chatInput.value }
messages.value.push(message)
post(message)
post({ file_path: file.value?.file_path, file_name: file.value?.file_name, chart_content: chatInput.value })
chatInput.value = ''
}
const chatRef = ref()
......@@ -43,7 +43,10 @@ watch(messages.value, () => nextTick(() => scrollToBottom()))
<template v-for="(item, index) in messages" :key="index">
<div class="ai-message-item" :class="item.role" v-if="item.role !== 'system'">
<div class="ai-message__avatar"><img :src="item.role === 'assistant' ? '/images/ai_avatar_bot.png' : '/images/ai_avatar_user.png'" /></div>
<div class="ai-message__content" v-html="item.content"></div>
<div class="ai-message__content">
<iframe :srcdoc="item.content" frameborder="0" v-if="item.type === 'html'" width="900" height="500"></iframe>
<div v-html="item.content" v-else></div>
</div>
</div>
</template>
<div class="ai-message-item" v-if="isLoading">
......@@ -76,7 +79,7 @@ watch(messages.value, () => nextTick(() => scrollToBottom()))
flex: none;
}
.el-upload-dragger {
padding: 80px 0;
padding: 80px 20px;
}
}
}
......
......@@ -68,6 +68,19 @@ const learnURL = import.meta.env.VITE_SAAS_LEARN_URL
margin-left: 0 !important;
}
}
.system-amo {
.bg {
background: url(@/assets/images/amo_home_student_bg.png) no-repeat center center;
background-size: contain;
}
.link1,
.select1 {
display: none;
}
.select2 {
margin-left: 0 !important;
}
}
.system-x {
.bg {
background: url(@/assets/images/x_home_student_bg.png) no-repeat center center;
......
......@@ -2,6 +2,9 @@
import type { Contest } from '../types'
import ContestItem from '../components/ContestItem.vue'
import { getMyContestList, getContestList } from '../api'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
let myContestList = $ref<Contest[]>([])
function fetchMyList() {
getMyContestList().then(res => {
......@@ -27,7 +30,7 @@ onMounted(() => {
</div>
<el-empty description="暂无数据" v-else />
</AppCard>
<AppCard title="可参与赛项">
<AppCard title="可参与赛项" v-if="appConfig.hideAvailableEvents !== true">
<div class="contest-list" v-if="contestList.length">
<ContestItem :data="item" v-for="item in contestList" :key="item.id"></ContestItem>
</div>
......
......@@ -7,6 +7,8 @@ import { upload } from '@/utils/upload'
import { getContest, getExperimentRecord, uploadExperimentPicture } from '../api'
import dayjs from 'dayjs'
import { useCookies } from '@vueuse/integrations/useCookies'
import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
const Book = defineAsyncComponent(() => import('../components/Book.vue'))
const Video = defineAsyncComponent(() => import('../components/Video.vue'))
......@@ -117,7 +119,7 @@ const competitionUrl = computed(() => {
return url.includes('?') ? `${url}&token=${cookies.get('TGC')}` : `${url}?token=${cookies.get('TGC')}`
})
let isHeadShow = $ref(true)
let isHeadShow = $ref(!appConfig.hideContestToolbar)
const handleShowHead = function () {
isHeadShow = !isHeadShow
}
......@@ -144,7 +146,7 @@ const reportDialogVisible = $ref(false)
<!-- <Book :competition_id="id" :key="resizeKey"></Book> -->
<Book :competition_id="id" :experiment_id="getExperimentId"></Book>
</el-tab-pane>
<el-tab-pane label="操作视频" lazy v-if="false">
<el-tab-pane label="操作视频" lazy>
<Video :competition_id="id"></Video>
</el-tab-pane>
<el-tab-pane label="讨论交流" lazy>
......@@ -153,14 +155,14 @@ const reportDialogVisible = $ref(false)
<el-tab-pane label="过程与结果" lazy>
<Result :competition_id="id" @update="fetchInfo"></Result>
</el-tab-pane>
<el-tab-pane label="实操试卷" lazy>
<el-tab-pane label="实操试卷" lazy v-if="appConfig.hidePracticalTestPaper !== true">
<Exam :competition_id="id"></Exam>
</el-tab-pane>
</el-tabs>
</div>
</template>
<template #right>
<AppCard v-if="isHeadShow">
<AppCard v-if="isHeadShow" style="margin-bottom: 20px">
<el-row justify="space-between">
<el-button type="primary" :icon="HomeFilled" @click="handleBackHome">返回首页</el-button>
<div>
......@@ -235,7 +237,6 @@ const reportDialogVisible = $ref(false)
position: relative;
flex: 1;
width: 100%;
margin-top: 20px;
background: #f8f9fa;
}
.iframe {
......
......@@ -27,7 +27,7 @@ const selectChange = function () {
</script>
<template>
<AppCard>
<h1>2023年全国大学生商业数据分析与应用大赛成绩查询结果</h1>
<h1>2024 年湖北工匠杯成绩查询</h1>
<div style="display: flex; align-items: center; padding-top: 30px">
<span style="margin-right: 10px">赛项</span>
<el-select style="width: 320px" v-model="eventValue" placeholder="请选择" @change="selectChange">
......
......@@ -5,7 +5,6 @@ import { useAppConfig } from '@/composables/useAppConfig'
const appConfig = useAppConfig()
const httpRequest = axios.create({
timeout: 60000,
withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
......
import fs from 'node:fs'
import path from 'node:path'
// import fs from 'node:fs'
// import path from 'node:path'
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
......@@ -7,6 +7,7 @@ import vue from '@vitejs/plugin-vue'
// import checker from 'vite-plugin-checker'
import AutoImport from 'unplugin-auto-import/vite'
import ReactivityTransform from '@vue-macros/reactivity-transform/vite'
import mkcert from 'vite-plugin-mkcert'
export default defineConfig(() => ({
// base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
......@@ -17,16 +18,17 @@ export default defineConfig(() => ({
dts: true,
eslintrc: { enabled: true }
}),
ReactivityTransform()
ReactivityTransform(),
mkcert()
// checker({ vueTsc: true, eslint: { lintCommand: 'eslint "./src/**/*.{vue,js,jsx,ts,tsx}"' } })
],
server: {
open: true,
host: 'dev.ezijing.com',
https: {
key: fs.readFileSync(path.join(__dirname, './https/ezijing.com.key')),
cert: fs.readFileSync(path.join(__dirname, './https/ezijing.com.pem'))
},
// https: {
// key: fs.readFileSync(path.join(__dirname, './https/ezijing.com.key')),
// cert: fs.readFileSync(path.join(__dirname, './https/ezijing.com.pem'))
// },
proxy: {
'/api': 'https://saas-lab.ezijing.com'
// '/api/resource': {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论