fix: update development environment API and refine student training styles

fix/exam-question-access-material-excel-delete-draft
qinzhenpen 1 month ago
parent 4cb3b8756b
commit 6dc199a4fc

@ -9,13 +9,13 @@
# 页面标题 # 页面标题
VITE_APP_TITLE = 电商互联网产品开发实训系统 VITE_APP_TITLE = 电商互联网产品开发实训系统
# 开发环境配置 # 开发环境配置
VITE_APP_ENV = 'development' VITE_APP_ENV = 'development'
# 若依管理系统/开发环境 # 若依管理系统/开发环境
VITE_APP_BASE_API = '/' VITE_APP_BASE_API = 'http://120.79.161.177:7548'
# VITE_APP_BASE_API = 'http://192.168.2.6:7548/' # VITE_APP_BASE_API = 'http://192.168.2.6:7548/'
# VITE_APP_BASE_API = 'http://192.168.2.6:7548/' # VITE_APP_BASE_API = 'http://192.168.2.6:7548/'
# VITE_APP_BASE_API = 'http://118.31.7.2:7548/' # VITE_APP_BASE_API = 'http://118.31.7.2:7548/'
VITE_APP_AI_API = 'https://whmnjy.sztzjy.com:8764/' VITE_APP_AI_API = 'https://whmnjy.sztzjy.com:8764/'
VITE_APP_ENV = 'development' VITE_APP_ENV = 'development'
VITE_APP_BASE_API = '/' # VITE_APP_BASE_API = '/'

@ -1,12 +1,12 @@
.student-training-shell { .student-training-shell {
--student-step-height: 46px; --student-step-height: 40px;
--student-step-font: 14px; --student-step-font: 13px;
--student-action-height: 42px; --student-action-height: 38px;
--student-action-font: 16px; --student-action-font: 15px;
--student-upload-height: 40px; --student-upload-height: 36px;
--student-upload-font: 14px; --student-upload-font: 13px;
--student-aux-height: 42px; --student-aux-height: 38px;
--student-aux-font: 14px; --student-aux-font: 13px;
--student-radius-sm: 6px; --student-radius-sm: 6px;
--student-radius-pill: 999px; --student-radius-pill: 999px;
--student-accent: #20a4d9; --student-accent: #20a4d9;
@ -30,12 +30,12 @@
display: grid !important; display: grid !important;
grid-template-columns: minmax(660px, 1fr) 300px !important; grid-template-columns: minmax(660px, 1fr) 300px !important;
align-items: start !important; align-items: start !important;
gap: 14px !important; gap: 10px !important;
width: 100% !important; width: 100% !important;
max-width: none !important; max-width: none !important;
min-height: calc(100vh - 90px) !important; min-height: calc(100vh - 90px) !important;
margin: 0 !important; margin: 0 !important;
padding: 14px 14px 20px !important; padding: 10px 10px 16px !important;
background: #06111d !important; background: #06111d !important;
} }
@ -46,27 +46,27 @@
*/ */
.student-training-shell > main { .student-training-shell > main {
min-width: 0 !important; min-width: 0 !important;
padding: 20px !important; padding: 14px !important;
border-radius: 24px !important; border-radius: 20px !important;
} }
.student-training-shell > main > .task-card, .student-training-shell > main > .task-card,
.student-training-shell > main > .process-workbench, .student-training-shell > main > .process-workbench,
.student-training-shell > main > .factors-workbench, .student-training-shell > main > .factors-workbench,
.student-training-shell > main > .workbench { .student-training-shell > main > .workbench {
padding: 14px 16px !important; padding: 10px 12px !important;
border-radius: 20px !important; border-radius: 16px !important;
} }
.student-training-shell .step-card, .student-training-shell .step-card,
.student-training-shell .process-step-card, .student-training-shell .process-step-card,
.student-training-shell .factor-step-card { .student-training-shell .factor-step-card {
padding: 16px !important; padding: 12px !important;
border-radius: 20px !important; border-radius: 16px !important;
} }
.student-training-shell .task-header { .student-training-shell .task-header {
margin-bottom: 16px !important; margin-bottom: 12px !important;
} }
.student-training-shell .task-header .task-title { .student-training-shell .task-header .task-title {
@ -104,10 +104,10 @@
flex-wrap: nowrap !important; flex-wrap: nowrap !important;
align-items: center !important; align-items: center !important;
justify-content: space-between !important; justify-content: space-between !important;
gap: 6px !important; gap: 4px !important;
width: 100% !important; width: 100% !important;
margin: 0 0 18px !important; margin: 0 0 12px !important;
padding: 6px !important; padding: 4px !important;
border: 1px solid var(--student-panel-border) !important; border: 1px solid var(--student-panel-border) !important;
border-radius: var(--student-radius-pill) !important; border-radius: var(--student-radius-pill) !important;
background: rgba(0, 20, 35, 0.65) !important; background: rgba(0, 20, 35, 0.65) !important;
@ -128,7 +128,7 @@
width: auto !important; width: auto !important;
min-height: var(--student-step-height) !important; min-height: var(--student-step-height) !important;
height: var(--student-step-height) !important; height: var(--student-step-height) !important;
padding: 0 14px !important; padding: 0 10px !important;
border: 1px solid transparent !important; border: 1px solid transparent !important;
border-radius: var(--student-radius-pill) !important; border-radius: var(--student-radius-pill) !important;
color: #bed5e4 !important; color: #bed5e4 !important;
@ -154,10 +154,10 @@
flex-wrap: nowrap !important; flex-wrap: nowrap !important;
align-items: center !important; align-items: center !important;
justify-content: space-between !important; justify-content: space-between !important;
gap: 6px !important; gap: 4px !important;
width: 100% !important; width: 100% !important;
margin: 0 0 24px !important; margin: 0 0 16px !important;
padding: 6px !important; padding: 4px !important;
border: 1px solid var(--student-panel-border) !important; border: 1px solid var(--student-panel-border) !important;
border-radius: 60px !important; border-radius: 60px !important;
background: rgba(0, 35, 58, 0.66) !important; background: rgba(0, 35, 58, 0.66) !important;
@ -168,11 +168,11 @@
flex: 1 1 0 !important; flex: 1 1 0 !important;
align-items: center !important; align-items: center !important;
justify-content: center !important; justify-content: center !important;
gap: 10px !important; gap: 6px !important;
min-width: 0 !important; min-width: 0 !important;
min-height: var(--student-step-height) !important; min-height: var(--student-step-height) !important;
height: var(--student-step-height) !important; height: var(--student-step-height) !important;
padding: 0 10px !important; padding: 0 8px !important;
border: 1px solid transparent !important; border: 1px solid transparent !important;
border-radius: var(--student-radius-pill) !important; border-radius: var(--student-radius-pill) !important;
color: #bed5e4 !important; color: #bed5e4 !important;
@ -209,10 +209,10 @@
.training-step-area { .training-step-area {
width: 100% !important; width: 100% !important;
margin: 18px 0 24px !important; margin: 12px 0 16px !important;
padding: 18px !important; padding: 12px !important;
border: 1px solid rgba(0, 180, 255, 0.3) !important; border: 1px solid rgba(0, 180, 255, 0.3) !important;
border-radius: 22px !important; border-radius: 16px !important;
background: rgba(0, 35, 58, 0.66) !important; background: rgba(0, 35, 58, 0.66) !important;
box-shadow: inset 0 1px 0 rgba(95, 232, 255, 0.12) !important; box-shadow: inset 0 1px 0 rgba(95, 232, 255, 0.12) !important;
} }
@ -310,8 +310,8 @@
flex-wrap: wrap !important; flex-wrap: wrap !important;
justify-content: center !important; justify-content: center !important;
align-items: center !important; align-items: center !important;
gap: 52px !important; gap: 20px !important;
margin-top: 36px !important; margin-top: 20px !important;
padding: 0 !important; padding: 0 !important;
} }
@ -355,8 +355,8 @@
flex-wrap: wrap !important; flex-wrap: wrap !important;
justify-content: center !important; justify-content: center !important;
align-items: center !important; align-items: center !important;
gap: 28px !important; gap: 20px !important;
margin-top: 28px !important; margin-top: 20px !important;
} }
.student-training-shell .task-actions > button { .student-training-shell .task-actions > button {
@ -378,8 +378,8 @@
flex-wrap: wrap !important; flex-wrap: wrap !important;
justify-content: center !important; justify-content: center !important;
align-items: center !important; align-items: center !important;
gap: 28px !important; gap: 20px !important;
margin-top: 28px !important; margin-top: 20px !important;
} }
.student-training-shell .taskfooter .el-button { .student-training-shell .taskfooter .el-button {
@ -397,8 +397,8 @@
.student-training-shell .training-actions, .student-training-shell .training-actions,
.student-training-shell .task-actions, .student-training-shell .task-actions,
.student-training-shell .taskfooter { .student-training-shell .taskfooter {
gap: 28px !important; gap: 20px !important;
margin-top: 28px !important; margin-top: 20px !important;
} }
.student-training-shell .training-actions .el-button.training-action, .student-training-shell .training-actions .el-button.training-action,
@ -558,11 +558,11 @@
.legacy-training-page { .legacy-training-page {
display: grid !important; display: grid !important;
box-sizing: border-box !important; box-sizing: border-box !important;
grid-template-columns: minmax(0, 1fr) 300px !important; grid-template-columns: minmax(0, 1fr) 280px !important;
gap: 14px !important; gap: 10px !important;
align-items: start !important; align-items: start !important;
width: 100% !important; width: 100% !important;
padding: 14px !important; padding: 10px !important;
background: #06111d !important; background: #06111d !important;
} }
@ -575,7 +575,7 @@
grid-column: 2 !important; grid-column: 2 !important;
grid-row: 1 / span 20 !important; grid-row: 1 / span 20 !important;
position: sticky !important; position: sticky !important;
top: 18px !important; top: 14px !important;
} }
.legacy-training-page > .training-intro, .legacy-training-page > .training-intro,

@ -1,71 +1,123 @@
<template> <template>
<div class="material-actions"> <div class="material-actions">
<el-button v-if="material && previewable" class="material-button" @click="previewVisible = true"> <el-button v-if="material && previewable" class="material-button" @click="previewVisible = true">
<el-icon><View /></el-icon>预览案例资料 <el-icon>
</el-button> <View />
<el-button class="material-button" :plain="!material?.url" @click="handleDownload"> </el-icon>预览案例资料
<el-icon><Download /></el-icon>下载案例资料 </el-button>
</el-button> <el-button class="material-button" :plain="!material?.url" @click="handleDownload">
</div> <el-icon>
<Download />
</el-icon>下载案例资料
</el-button>
</div>
<el-dialog v-model="previewVisible" :title="material?.name || '案例资料预览'" width="min(920px, 92vw)" destroy-on-close append-to-body> <el-dialog v-model="previewVisible" :title="material?.name || '案例资料预览'" width="min(920px, 92vw)" destroy-on-close
<div v-if="isImage" class="image-preview"><img :src="material.url" :alt="material.name" /></div> append-to-body>
<iframe v-else-if="isPdf" class="pdf-preview" :src="material.url" :title="material.name"></iframe> <div v-if="isImage" class="image-preview"><img :src="material.url" :alt="material.name" /></div>
</el-dialog> <iframe v-else-if="isPdf" class="pdf-preview" :src="material.url" :title="material.name"></iframe>
</el-dialog>
</template> </template>
<script setup> <script setup>
import { computed, ref } from "vue"; import { computed, ref } from "vue"
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus"
import { Download, View } from "@element-plus/icons-vue"; import { Download, View } from "@element-plus/icons-vue"
const props = defineProps({ materialName: { type: String, default: "" }, materialUrl: { type: String, default: "" } }); const props = defineProps({ materialName: { type: String, default: "" }, materialUrl: { type: String, default: "" } })
const previewVisible = ref(false); const previewVisible = ref(false)
const material = computed(() => { const material = computed(() => {
const name = String(props.materialName || "").trim(); const name = String(props.materialName || "").trim()
const url = String(props.materialUrl || "").trim(); const url = String(props.materialUrl || "").trim()
if (!name || !url || (name === "案例资料.rar" && url === "/file/example.rar")) return null; if (!name || !url || (name === "案例资料.rar" && url === "/file/example.rar")) return null
return { name, url: normalizeMaterialUrl(url) }; return { name, url: normalizeMaterialUrl(url) }
}); })
const extension = computed(() => String(material.value?.name || material.value?.url || "").split(/[?#]/)[0].split(".").pop().toLowerCase()); const extension = computed(() => String(material.value?.name || material.value?.url || "").split(/[?#]/)[0].split(".").pop().toLowerCase())
const isImage = computed(() => ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"].includes(extension.value)); const isImage = computed(() => ["jpg", "jpeg", "png", "gif", "webp", "bmp", "svg"].includes(extension.value))
const isPdf = computed(() => extension.value === "pdf"); const isPdf = computed(() => extension.value === "pdf")
const previewable = computed(() => isImage.value || isPdf.value); const previewable = computed(() => isImage.value || isPdf.value)
function normalizeMaterialUrl(rawUrl) { function normalizeMaterialUrl(rawUrl) {
const url = String(rawUrl || "").trim(); const url = String(rawUrl || "").trim()
if (!url) return ""; if (!url) return ""
try { try {
const parsed = new URL(url); const parsed = new URL(url)
if (["localhost", "127.0.0.1", "::1"].includes(parsed.hostname) && parsed.pathname.startsWith("/file/")) { if (["localhost", "127.0.0.1", "::1"].includes(parsed.hostname) && parsed.pathname.startsWith("/file/")) {
return `${parsed.pathname}${parsed.search}${parsed.hash}`; return `${parsed.pathname}${parsed.search}${parsed.hash}`
}
} catch {
// Relative file paths are already resolved through the current deployment or Vite proxy.
} }
} catch { return url
// Relative file paths are already resolved through the current deployment or Vite proxy.
}
return url;
} }
async function handleDownload() { async function handleDownload() {
if (!material.value?.url) { ElMessage.warning("暂无案例资料"); return; } if (!material.value?.url) { ElMessage.warning("暂无案例资料"); return }
try { try {
const response = await fetch(material.value.url); const response = await fetch(material.value.url)
if (!response.ok) throw new Error(`HTTP ${response.status}`); if (!response.ok) throw new Error(`HTTP ${response.status}`)
const objectUrl = URL.createObjectURL(await response.blob()); const objectUrl = URL.createObjectURL(await response.blob())
const link = document.createElement("a"); const link = document.createElement("a")
link.href = objectUrl; link.href = objectUrl
link.download = material.value.name; link.download = material.value.name
document.body.appendChild(link); document.body.appendChild(link)
link.click(); link.click()
document.body.removeChild(link); document.body.removeChild(link)
URL.revokeObjectURL(objectUrl); URL.revokeObjectURL(objectUrl)
} catch (error) { } catch (error) {
console.warn("Case material download failed", error); console.warn("Case material download failed", error)
ElMessage.error("案例资料文件不存在或暂不可下载,请联系管理员重新上传。"); ElMessage.error("案例资料文件不存在或暂不可下载,请联系管理员重新上传。")
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.material-actions{display:flex;flex-wrap:wrap;gap:8px}.material-button{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:32px;padding:0 10px;border-color:rgba(99,217,255,.55);border-radius:7px;color:#e9fbff;background:rgba(17,126,178,.36);font-size:13px;font-weight:800}.material-button:hover{color:#e9fbff;border-color:rgba(116,235,255,.85);background:rgba(18,146,204,.5)}.image-preview{display:flex;justify-content:center;max-height:68vh;overflow:auto;background:#0b1720}.image-preview img{display:block;max-width:100%;height:auto;object-fit:contain}.pdf-preview{width:100%;height:68vh;border:0;background:#fff} .material-actions {
display: flex;
flex-wrap: wrap;
gap: 6px
}
.material-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
min-height: 28px;
padding: 0 8px;
border-color: rgba(99, 217, 255, .55);
border-radius: var(--student-radius-sm);
color: #e9fbff;
background: rgba(17, 126, 178, .36);
font-size: var(--student-step-font);
font-weight: 800
}
.material-button:hover {
color: #e9fbff;
border-color: rgba(116, 235, 255, .85);
background: rgba(18, 146, 204, .5)
}
.image-preview {
display: flex;
justify-content: center;
max-height: 68vh;
overflow: auto;
background: #0b1720
}
.image-preview img {
display: block;
max-width: 100%;
height: auto;
object-fit: contain
}
.pdf-preview {
width: 100%;
height: 68vh;
border: 0;
background: #fff
}
</style> </style>

Loading…
Cancel
Save