教育培训

dev
LiuJiaNan 2024-03-13 13:47:04 +08:00
parent f570e9dcc6
commit 566eee266e
25 changed files with 3809 additions and 1122 deletions

View File

@ -6,8 +6,11 @@ import {
getDepartmentTree,
getLevelsCorp,
getElectronicFenceTree,
getListSelectTree,
getDepartmentzTree,
getTrainingType,
getIndustryType,
getPostType,
getTrainingLevel,
} from "@/request/data_dictionary.js";
import { ref } from "vue";
@ -37,34 +40,6 @@ export const layoutFnGetEnterpriseScale = async () => {
});
return ref(resData.list);
};
// 培训行业类型
export const layoutFnGetTrainingIndustryType = async () => {
const resData = await getLevelsByParentId({
parentId: "052369aa22d242118236cde52d0c67ea",
});
return ref(JSON.parse(resData.zTreeNodes));
};
// 培训岗位类型
export const layoutFnGetTrainingPostType = async () => {
const resData = await getLevelsByParentId({
parentId: "f6a7c4f5602f46e291d06b1390a3f820",
});
return ref(JSON.parse(resData.zTreeNodes));
};
// 培训板块类型
export const layoutFnGetTrainingPlateType = async () => {
const resData = await getLevelsByParentId({
parentId: "d538d11e4eec409ab428f5d2f3c67c24",
});
return ref(JSON.parse(resData.zTreeNodes));
};
// 大纲类型
export const layoutFnGetOutlineType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "3c0d9b5e74834adfacb76139e5d731e5",
});
return ref(resData.list);
};
// 管控措施分类1
export const layoutFnGetControlMeasures1 = async () => {
const resData = await getLevelsByParentId({
@ -79,27 +54,6 @@ export const layoutFnGetControlMeasures2 = async () => {
});
return ref(JSON.parse(resData.zTreeNodes));
};
// 受限空间类型
export const layoutFnGetTypeOfConfinedSpace = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "dad23a464729485ba364994942db83cc",
});
return ref(resData.list);
};
// 风险等级
export const layoutFnGetRiskLevel = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "a178357ce6ce4d6a9ac95def6aca3a14",
});
return ref(resData.list);
};
// 风险成因
export const layoutFnGetRiskCauses = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "1bacbc4c1f6544718519c0d470dfeb62",
});
return ref(resData.list);
};
// 民族
export const layoutFnGetNation = async () => {
const resData = await getLevels({
@ -220,13 +174,6 @@ export const layoutFnGetAccidentType = async () => {
});
return ref(resData.list);
};
// 事故级别
export const layoutFnGetAccidentLevel = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "9a77f67eff4c49b19454bf3e40e2ae57",
});
return ref(resData.list);
};
// 风险分级
export const layoutFnGetRiskClassification = async () => {
const resData = await getLevels({
@ -234,146 +181,6 @@ export const layoutFnGetRiskClassification = async () => {
});
return ref(resData.list);
};
// 承诺书级别
export const layoutFnGetCommitmentLevel = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "8c5064693fba4dbca1cbfea631cd097a",
});
return ref(resData.list);
};
// 申报等级
export const layoutFnGetDeclarationGrade = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "246a2fe3af964eb18f0e005339e334dd",
});
return ref(resData.list);
};
// 重大危险源类型
export const layoutFnGetMajorHazardSourceType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "6f7469fadb75401e9928edb0ab19b17e",
});
return ref(resData.list);
};
// 监测数据类型
export const layoutFnGetEquipmentType = async () => {
const resData = await getLevelsCorp({
DICTIONARIES_ID: "6a724d36c3ad416fad22049d932c1987",
});
return ref(resData.list);
};
// 学习园地类型
export const layoutFnGetLearningGardenType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "5aa989ad8fd54bef862c1b096a3b07d8",
});
return ref(resData.list);
};
// 学习园地学习类型
export const layoutFnGetLearningGardenLearningType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "c4f00e7ed9334789ba2fb4795f54bae2",
});
return ref(resData.list);
};
// 培训类型
export const layoutFnGetTrainingType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "10ffcf646d354ff0a94630c7435e73fe",
});
return ref(resData.list);
};
// 适用领域
export const layoutFnGetApplicableFields = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "ab52643da4714042be2ebfb880097330",
});
return ref(resData.list);
};
// 预案类别
export const layoutFnGetPlanCategory = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "b2759070db524cc4ae2d60ee4b263dc3",
});
return ref(resData.list);
};
// 预案备案状态
export const layoutFnGetPlanFilingStatus = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "8d07af03be10473ca17e1516a8e860d2",
});
return ref(resData.list);
};
// 预案级别
export const layoutFnGetPlanLevel = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "ab54d50f12ef4f578bc29c3b980b83e5",
});
return ref(resData.list);
};
// 装备类别
export const layoutFnGetEquipmentCategory = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "716f792cfc354fcf81bf3c0e904fb4f8",
});
return ref(resData.list);
};
// 救援专业
export const layoutFnGetRescueSpecialty = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "6a724d36c3ad416fad22049d932c329e",
});
return ref(resData.list);
};
// 队伍属性
export const layoutFnGetTeamAttribute = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "afde28dd39c841d984fb331116781ff3",
});
return ref(resData.list);
};
// 队伍类别
export const layoutFnGetTeamCategory = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "ef2f9a53723b4531968279fe4816be3d",
});
return ref(resData.list);
};
// 队伍等级
export const layoutFnGetTeamGrade = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "4dca6d39490d4845ba7b02df2ab68aa4",
});
return ref(resData.list);
};
// 队伍级别
export const layoutFnGetTeamLevel = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "bfd057e59c124b42ac93e13c6a30b85e",
});
return ref(resData.list);
};
// 特种设备类型
export const layoutFnGetSpecialEquipmentType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "cffb3d28d48548328ddd78976610b05d",
});
return ref(resData.list);
};
// 特种设备状态
export const layoutFnGetSpecialEquipmentState = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "0feb448f975e422fafbf5db20c0b85c9",
});
return ref(resData.list);
};
// 巡检周期
export const layoutFnGetInspectionCycle = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "3017f7f6f6794f23bf15d026a1e55426",
});
return ref(resData.list);
};
// 部门树
export const layoutFnGetDepartmentTree = async (params) => {
const resData = await getDepartmentTree(params);
@ -389,13 +196,6 @@ export const layoutFnGetElectronicFenceTree = async (params) => {
const resData = await getElectronicFenceTree(params);
return ref(JSON.parse(resData.zTreeNodes));
};
// 检查类型树
export const layoutFnGetInspectionTypeTree = async () => {
const resData = await getListSelectTree({
DICTIONARIES_ID: "60e6481d96e44a5390ff5c347c4d1ffe",
});
return ref(JSON.parse(resData.zTreeNodes));
};
// 无法确定DICTIONARIES_ID的数据字典
export const layoutFnGetLevels = async (DICTIONARIES_ID) => {
const resData = await getLevels({ DICTIONARIES_ID });
@ -406,15 +206,6 @@ export const layoutFnGetLevelsAndChildrenNumber = async (DICTIONARIES_ID) => {
const resData = await getLevelsAndChildrenNumber({ DICTIONARIES_ID });
return ref(resData.list);
};
// 相关方作业类别
export const layoutFnGetPersonnelManagementJobType = async () => {
const resData = await getLevels({
DICTIONARIES_ID: "a8bfd4554af54ee2b816f3007d8baea6",
});
return ref(resData.list);
};
// 年检周期
export const layoutFnGetAnnualInspectionCycle = async () => {
const resData = await getLevels({
@ -422,7 +213,6 @@ export const layoutFnGetAnnualInspectionCycle = async () => {
});
return ref(resData.list);
};
// 保险公司
export const layoutFnGetInsuranceCompany = async () => {
const resData = await getLevels({
@ -430,3 +220,24 @@ export const layoutFnGetInsuranceCompany = async () => {
});
return ref(resData.list);
};
// 培训类型
export const layoutFnGetTrainingType = async (params) => {
const resData = await getTrainingType(params);
return ref(resData.trainingtypelist);
};
// 行业类型
export const layoutFnGetIndustryType = async (params) => {
const resData = await getIndustryType(params);
return ref(JSON.parse(resData.zTreeNodes));
};
// 岗位类型
export const layoutFnGetPostType = async (params) => {
const resData = await getPostType(params);
return ref(resData.varList);
};
// 培训级别
export const layoutFnGetTrainingLevel = async (params) => {
const resData = await getTrainingLevel(params);
return ref(resData.varList);
};

View File

@ -1,46 +0,0 @@
<template>
<el-tree-select
ref="treeSelectRef"
v-model="modelValue"
:data="data"
:props="{
value: 'id',
children: 'nodes',
label: 'name',
}"
node-key="id"
:render-after-expand="false"
accordion
check-strictly
:clearable="true"
/>
</template>
<script setup>
import { layoutFnGetInspectionTypeTree } from "@/assets/js/data_dictionary";
import { ref } from "vue";
import { useVModel } from "@vueuse/core";
defineOptions({
name: "LayoutInspectionType",
});
const props = defineProps({
modelValue: {
type: String,
required: true,
default: "",
},
});
const emits = defineEmits(["update:modelValue"]);
const modelValue = useVModel(props, "modelValue", emits);
const treeSelectRef = ref(null);
const getCurrentNode = () => {
return treeSelectRef.value.getCurrentNode();
};
defineExpose({
getCurrentNode,
});
const data = await layoutFnGetInspectionTypeTree();
</script>
<style scoped></style>

File diff suppressed because it is too large Load Diff

View File

@ -1,84 +0,0 @@
<template>
<div id="bi_container">
<div id="map" class="map_bg"></div>
</div>
</template>
<script setup>
import { onBeforeUnmount, onMounted } from "vue";
import { initMap, handleViewAlarm, handleMouseClick } from "./map";
import { useUserStore } from "@/pinia/user.js";
import { getEnterpriseInfo } from "@/request/enterprise_management.js";
const props = defineProps({
alarm: {
type: Object,
required: true,
default: null,
},
userList: {
type: Array,
required: true,
default: () => [],
},
});
const userStore = useUserStore();
const CORPINFO_ID = userStore.getUserInfo.CORPINFO_ID;
onMounted(async () => {
const corp = await getEnterpriseInfo({ CORPINFO_ID });
initMap(corp.pd);
handleMouseClick();
handleViewAlarm(props.alarm, props.userList);
});
onBeforeUnmount(() => {
window.$scene = null;
window.$icy = null;
window.$carmer = null;
});
</script>
<style scoped lang="scss">
#bi_container {
width: 100%;
height: 600px;
color: #ffffff;
position: relative;
font-size: 14px;
.map_bg {
width: 100%;
height: 540px;
}
.options {
margin-top: 10px;
display: flex;
align-items: center;
padding: 10px;
.option {
margin-right: 20px;
cursor: pointer;
text-align: center;
img {
width: 46px;
height: 46px;
}
.label {
margin-top: 10px;
font-size: 12px;
}
}
.id {
background-color: #1b284a;
padding: 10px;
border-radius: 4px;
height: 37px;
}
}
}
</style>

View File

@ -1,152 +0,0 @@
<template>
<div id="bi_container">
<div id="map" class="map_bg"></div>
<div class="options">
<template v-if="type === 1">
<div class="option" @click="confrim">
<div>
<img src="/src/assets/images/map_tools/icon1.png" alt="" />
</div>
<div class="label">完成绘制</div>
</div>
<div class="option" @click="clear">
<div>
<img src="/src/assets/images/map_tools/icon2.png" alt="" />
</div>
<div class="label">清除绘制</div>
</div>
<div class="option" @click="reduction">
<div>
<img src="/src/assets/images/map_tools/icon3.png" alt="" />
</div>
<div class="label">还原建筑</div>
</div>
</template>
<div class="id">当前选中楼层id{{ model_id }}</div>
</div>
</div>
</template>
<script setup>
import { useVModels } from "@vueuse/core";
import { onBeforeUnmount, onMounted, ref } from "vue";
import {
clearEnclosure,
handleEnclosure,
handleMouseClick,
handlePut,
initMap,
reduction,
showEnclosure,
} from "./map";
import { useUserStore } from "@/pinia/user.js";
import { getEnterpriseInfo } from "@/request/enterprise_management.js";
const props = defineProps({
type: {
type: Number,
default: 1,
},
positions: {
type: Array,
default: () => [],
},
position: {
type: Array,
default: () => [],
},
modUuid: {
type: String,
default: "",
},
});
const userStore = useUserStore();
const CORPINFO_ID = userStore.getUserInfo.CORPINFO_ID;
const data = [];
const model_id = ref(0);
const emits = defineEmits([
"update:positions",
"update:position",
"update:modUuid",
]);
const { positions, position, modUuid } = useVModels(props, emits);
const confrim = () => {
showEnclosure(data);
positions.value = [...data];
modUuid.value = model_id.value;
};
const clear = () => {
clearEnclosure(data);
data.length = 0;
positions.value = [];
};
onMounted(async () => {
const corp = await getEnterpriseInfo({ CORPINFO_ID });
initMap(corp.pd);
handleMouseClick(model_id);
if (props.type === 1) {
handleEnclosure(data);
} else {
handlePut(position);
}
});
onBeforeUnmount(() => {
window.$scene = null;
window.$icy = null;
window.$carmer = null;
});
</script>
<style scoped lang="scss">
#bi_container {
width: 100%;
height: 600px;
color: #ffffff;
position: relative;
font-size: 14px;
.map_bg {
width: 100%;
height: 540px;
}
.options {
margin-top: 10px;
display: flex;
align-items: center;
padding: 10px;
.option {
margin-right: 20px;
cursor: pointer;
text-align: center;
img {
width: 46px;
height: 46px;
}
.label {
margin-top: 10px;
font-size: 12px;
}
}
.id {
background-color: #1b284a;
padding: 10px;
border-radius: 4px;
height: 37px;
}
}
}
:deep {
div#menu {
button {
display: none;
}
}
}
</style>

View File

@ -1,167 +0,0 @@
import axios from "axios";
import { useUserStore } from "@/pinia/user";
import pinia from "@/pinia";
const userStore = useUserStore(pinia);
const pls_ip = userStore.getUserInfo.POST_URL;
const url = pls_ip.replace("8084", "9000") + "/buildr/public/models/glb/";
export class Loadglb {
static modelMap = {};
constructor(icy) {
this.icy = icy;
this.model_def_group = null;
this.scene = null;
this.idList = [];
this.entityList = [];
this.number = 0;
this.building_group = null;
this.groundHeight = 0.2; // 设置地面的高度
}
async fetchData() {
try {
const response = await axios.get(
pls_ip.replace("8084", "9000") +
"/buildr/public/models/scene_000001.json"
);
// 将获取的数据保存到实例的属性中
this.scene = response.data.scene;
this.model_def_group = response.data.scene.model_def_group;
const map = new Map();
this.model_def_group.forEach((i) => {
map.set(i.guid.split("-")[3], { name: i.name, id: i.guid });
});
this.scene.building_group.forEach((i) => {
i.layer_group.forEach((a) => {
a.modelName = map.get(a.guid[0].split("-")[3]);
});
});
this.building_group = this.scene.building_group;
// console.log(this.building_group);
} catch (error) {
// 处理可能错误
console.error(error);
}
}
async model(Model) {
try {
// 执行 axios 请求并等待 fetchData 函数的异步操作完成
await this.fetchData();
const modelList = {};
this.scene.model_def_group.forEach((item) => {
modelList[item.guid] = item.name;
});
const modelTotal = this.scene.building_group.length;
this.createModel(Model, modelList, modelTotal);
this.scene.ground_group.forEach((ground) => {
const lon = ground.transform.translate.x;
const lat = ground.transform.translate.y;
const scale = ground.transform.scale;
ground.model_ref_group.forEach((layer) => {
// console.log(layer,'111');
const height = this.groundHeight;
const unit = {
lon,
lat,
scale,
id: layer.model_def_guid,
};
this.logNum([layer], height, unit, modelList, Model);
});
});
} catch (error) {
// 处理可能的错误
console.error(error);
}
}
createModel(Model, modelList, modelTotal) {
this.scene.building_group.forEach((building, index) => {
if (
Number(this.number * 2) <= index &&
index < Number((this.number + 1) * 2)
) {
const lon = building.transform.translate.x;
const lat = building.transform.translate.y;
const scale = building.transform.scale;
building.layer_group.forEach((layer) => {
// console.log(layer,'111');
const height = Number(layer.height[0] + this.groundHeight);
const unit = {
lon,
lat,
scale,
id: layer.guid[0],
};
this.logNum(layer.model_ref_group, height, unit, modelList, Model);
});
}
});
if (this.number * 2 < modelTotal) {
const throttle = setTimeout(() => {
this.number++;
this.createModel(Model, modelList, modelTotal);
sessionStorage.setItem("loading", (this.number * 2) / modelTotal);
clearTimeout(throttle);
}, 20);
if (
(this.number * 2) / modelTotal >= 0.5 &&
(this.number * 2) / modelTotal < 0.8
) {
sessionStorage.setItem("loadModel", "过半");
} else if ((this.number * 2) / modelTotal >= 0.8) {
sessionStorage.setItem("loadModel", "临底");
}
} else {
console.log(
"加载完成 ,此次加载:" +
modelTotal +
"栋建筑,共:" +
this.model_def_group.length +
"个模型"
);
sessionStorage.setItem("loadModel", "完成");
}
}
logNum(red, height, unit, modelList, Model) {
red.forEach((item) => {
if (this.idList.indexOf(item.model_def_guid) !== -1) {
return;
}
this.idList.push(item.model_def_guid);
if (
Object.prototype.hasOwnProperty.call(
Loadglb.modelMap,
`${unit.id.split("-")[3].slice(0, unit.id.split("-")[3].length - 2)}`
)
) {
Loadglb.modelMap[
`${unit.id.split("-")[3].slice(0, unit.id.split("-")[3].length - 2)}`
].push(item.model_def_guid);
} else {
Loadglb.modelMap[
`${unit.id.split("-")[3].slice(0, unit.id.split("-")[3].length - 2)}`
] = [item.model_def_guid];
}
const m = new Model(this.icy, {
id: item.model_def_guid,
name: "建筑",
url: `${url}${modelList[item.model_def_guid]}.gltf`,
height,
lon: unit.lon,
lat: unit.lat,
scale: unit.scale,
angle: [90, 0, 0],
// angle: [97.4843053, 0, 0]
});
this.entityList.push(m);
});
}
getArr() {
return this.building_group;
}
}

View File

@ -1,404 +0,0 @@
import { Loadglb } from "./loadglb.js";
const loadMap = 3;
let $entityTransparent = [];
const clickModel = new Map();
let enclosure = null;
let entities = null;
export const initMap = (corp) => {
window.$scene = new window.CustomCesium.Scene(
"map",
corp.CORP_NAME,
Number(loadMap)
);
window.$icy = window.$scene.getIcy();
window.$carmer = new window.CustomCesium.Carmer(window.$icy);
window.$icy.viewer.targetFrameRate = "30";
window.$icy.viewer.scene.light = new window.Cesium.DirectionalLight({
// 去除时间原因影响模型颜色
direction: new window.Cesium.Cartesian3(
0.35492591601301104,
-0.8909182691839401,
-0.2833588392420772
),
});
const [wgsLat, wgsLon] = bd09ToWgs84(corp.LATITUDE, corp.LONGITUDE);
flyTo(wgsLon, wgsLat);
// 亮度设置
const stages = window.$icy.viewer.scene.postProcessStages;
window.$icy.viewer.scene.brightness =
window.$icy.viewer.scene.brightness ||
stages.add(window.Cesium.PostProcessStageLibrary.createBrightnessStage());
window.$icy.viewer.scene.brightness.enabled = true;
window.$icy.viewer.scene.brightness.uniforms.brightness = Number(1.05); // 此处亮度值为倍数
createGlb(); // 加载glb
};
const bd09ToWgs84 = (bdLat, bdLon) => {
const x_pi = (Math.PI * 3000.0) / 180.0;
const x = bdLon - 0.0065;
const y = bdLat - 0.006;
const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
const gcjLon = z * Math.cos(theta);
const gcjLat = z * Math.sin(theta);
let dlat = transformlat(gcjLon - 105.0, gcjLat - 35.0);
let dlng = transformlng(gcjLon - 105.0, gcjLat - 35.0);
const radlat = (gcjLat / 180.0) * Math.PI;
let magic = Math.sin(radlat);
magic = 1 - 0.006693421622965943 * magic * magic;
const sqrtmagic = Math.sqrt(magic);
dlat =
(dlat * 180.0) /
(((6378245.0 * (1 - 0.006693421622965943)) / (magic * sqrtmagic)) *
Math.PI);
dlng =
(dlng * 180.0) / ((6378245.0 / sqrtmagic) * Math.cos(radlat) * Math.PI);
const mglat = gcjLat + dlat;
const mglng = gcjLon + dlng;
const wgsLon = gcjLon * 2 - mglng;
const wgsLat = gcjLat * 2 - mglat;
return [wgsLat, wgsLon];
};
const transformlat = (lng, lat) => {
let ret =
-100.0 +
2.0 * lng +
3.0 * lat +
0.2 * lat * lat +
0.1 * lng * lat +
0.2 * Math.sqrt(Math.abs(lng));
ret +=
((20.0 * Math.sin(6.0 * lng * Math.PI) +
20.0 * Math.sin(2.0 * lng * Math.PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(lat * Math.PI) + 40.0 * Math.sin((lat / 3.0) * Math.PI)) *
2.0) /
3.0;
ret +=
((160.0 * Math.sin((lat / 12.0) * Math.PI) +
320 * Math.sin((lat * Math.PI) / 30.0)) *
2.0) /
3.0;
return ret;
};
// 纬度转换
const transformlng = (lng, lat) => {
let ret =
300.0 +
lng +
2.0 * lat +
0.1 * lng * lng +
0.1 * lng * lat +
0.1 * Math.sqrt(Math.abs(lng));
ret +=
((20.0 * Math.sin(6.0 * lng * Math.PI) +
20.0 * Math.sin(2.0 * lng * Math.PI)) *
2.0) /
3.0;
ret +=
((20.0 * Math.sin(lng * Math.PI) + 40.0 * Math.sin((lng / 3.0) * Math.PI)) *
2.0) /
3.0;
ret +=
((150.0 * Math.sin((lng / 12.0) * Math.PI) +
300.0 * Math.sin((lng / 30.0) * Math.PI)) *
2.0) /
3.0;
return ret;
};
// eslint-disable-next-line no-unused-vars
const flyTo = (lng, lat) => {
window.$carmer.flyTo({
// 视角飞入
maxHeight: 1500,
time: 1,
position: [lng, lat - 0.001, 200],
angle: [0, -60, 0],
});
};
const createGlb = () => {
// 加载glb建筑模型
const loadGlb = new Loadglb(window.$icy);
loadGlb.model(window.CustomCesium.Model);
};
// eslint-disable-next-line no-unused-vars
export const handleMouseClick = (model_id) => {
// 加载鼠标拾取
const $mouse = new window.CustomCesium.Mouse(window.$icy);
// 隐藏逻辑
$mouse.mouseLeft((model) => {
if (model._name === "建筑") {
if (model_id) model_id.value = model._id;
clickBuilding(model);
}
});
};
const clickBuilding = (model) => {
if (
Loadglb.modelMap[
`${model._id.split("-")[3].slice(0, model._id.split("-")[3].length - 2)}`
]
) {
if (
clickModel.get(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
) === undefined
) {
// 如果这个模型是第一次点击
const m =
Loadglb.modelMap[
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
]; // 整栋楼的id集合
// console.log(m, '整栋楼id');
if (m.length > 1) {
// 当前点击这建筑是否有多层
m.forEach((id) => {
const entity = window.$icy.viewer.entities.getById(id);
if (model.id !== id) {
// 排除当前点击楼层
if (entity._icy.height > model._icy.height) {
if (
$entityTransparent.map((a) => a._id).indexOf(entity._id) === -1
) {
$entityTransparent.push(entity);
}
// console.log(this.$entityTransparent, '被隐藏的模型楼层集合');
entity.model.show = false;
}
} else {
clickModel.set(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`,
model._icy.height
); // 存储这个模型数据
}
});
}
} else {
// 如果点击过这个模型
const m =
Loadglb.modelMap[
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
]; // 整栋楼的id集合
// console.log(m, '整栋楼id');
if (m.length > 1) {
// 当前点击这建筑是否有多层
if (
model._icy.height ===
clickModel.get(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
)
) {
m.forEach((id) => {
const entity = window.$icy.viewer.entities.getById(id);
entity.model.show = true;
clickModel.delete(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`
);
});
} else {
m.forEach((id) => {
const entity = window.$icy.viewer.entities.getById(id);
if (model.id !== id) {
// 排除当前点击楼层
if (entity._icy.height > model._icy.height) {
if (
$entityTransparent.map((a) => a._id).indexOf(entity._id) ===
-1
) {
$entityTransparent.push(entity);
}
// console.log(this.$entityTransparent, '被隐藏的模型楼层集合');
entity.model.show = false;
}
} else {
clickModel.set(
`${model._id
.split("-")[3]
.slice(0, model._id.split("-")[3].length - 2)}`,
model._icy.height
); // 存储这个模型数据
}
});
}
}
}
// model.model.color = new Cesium.Color(1, 1, 1, 0.6);
}
};
// 还原建筑物
export const reduction = () => {
// 揭盖一键还原
if ($entityTransparent.length !== 0) {
$entityTransparent = $entityTransparent.forEach((item) => {
item.model.color = new window.Cesium.Color(1, 1, 1, 1);
item.model.show = true;
});
$entityTransparent = [];
}
};
export const handleEnclosure = (positions) => {
const $mouse = new window.CustomCesium.Mouse(window.$icy);
enclosure = new window.CustomCesium.Enclosure(window.$icy);
// 编辑围栏使用鼠标控件参照demo.html
enclosure.start();
$mouse.mouseRight((e) => {
enclosure.add(e.lng, e.lat, e.alt);
positions.push([e.lng, e.lat, e.alt]);
});
};
export const clearEnclosure = () => {
if (enclosure) {
try {
enclosure.show(false);
} catch (e) {}
enclosure.clear();
enclosure = new window.CustomCesium.Enclosure(window.$icy);
enclosure.start();
}
};
export const showEnclosure = (positions) => {
enclosure.finish();
enclosure.showDataSource(
positions, // 数据
30, // 高度
"yellow" // 颜色默认黄色
);
enclosure.show(true);
};
export const handlePut = (pos) => {
const $mouse = new window.CustomCesium.Mouse(window.$icy);
$mouse.mouseRight((e) => {
if (entities && entities.children) {
entities.children.forEach((e) => {
e.destroy();
});
entities = null;
}
entities = new window.CustomCesium.GroupModel("摆放地图实例");
addEntity("put_entity_00001", "put_entity_00001", e.lng, e.lat, e.alt);
pos.value = [e.lng, e.lat, e.alt];
});
};
const addEntity = (id, name, lon, lat, height) => {
const obj = {};
obj.entity = window.$icy.viewer.entities.add(
new window.Cesium.Entity({
id,
name,
position: window.Cesium.Cartesian3.fromDegrees(lon, lat, height),
billboard: {
image: "src/assets/images/map/peoIcon_green.png",
height: 36,
width: 30,
verticalOrigin: window.Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: window.Cesium.HorizontalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
})
);
obj.show = (e) => {
obj.entity.show = e;
};
obj.destroy = () => {
window.$icy.viewer.entities.remove(obj.entity);
};
entities.add(obj);
};
export const handleViewAlarm = (alarm, userList) => {
const canvas = document.createElement("canvas");
const width = 100;
const height = 30;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "#000000";
ctx.fillRect(0, 0, 100, 50);
ctx.fillStyle = "#ff0000";
ctx.font = "normal bold 20px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText("聚集告警", width / 2, height / 2);
// 聚集圆锥特效
const Cone = new window.CustomCesium.Cone(window.$icy);
Cone.add(
[alarm.lon, alarm.lat, alarm.alt], // 坐标
alarm.color, // 圆锥颜色
{ width: 20, height: 40 }, // 圆锥的宽高
canvas, // 圆锥铭牌
"100201", // 圆锥实体id
"10020101" // 铭牌实体id
);
const labelPixelOffset = new window.Cesium.Cartesian2(0, -55);
userList.forEach((user) => {
const obj = {};
obj.entity = window.$icy.viewer.entities.add(
new window.Cesium.Entity({
id: "user_" + user.card,
name: user.psnName,
position: window.Cesium.Cartesian3.fromDegrees(
user.lon,
user.lat,
user.alt
),
billboard: {
image: "src/assets/images/map/peoIcon_blueImg.png",
height: 36,
width: 30,
verticalOrigin: window.Cesium.VerticalOrigin.BOTTOM,
horizontalOrigin: window.Cesium.HorizontalOrigin.CENTER,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
label: {
text: user.psnName,
font: "13px sans-serif",
pixelOffset: labelPixelOffset,
showBackground: true,
// 地图上扎点的名字背景色
// eslint-disable-next-line new-cap
backgroundColor: new window.Cesium.Color.fromCssColorString(
"rgba(20, 58, 142, 1)"
),
backgroundPadding: new window.Cesium.Cartesian2(7, 5),
disableDepthTestDistance: Number.POSITIVE_INFINITY,
},
})
);
obj.show = (e) => {
obj.entity.show = e;
};
obj.destroy = () => {
window.$icy.viewer.entities.remove(obj.entity);
};
});
};

View File

@ -10,6 +10,7 @@
:highlight-current-row="highlightCurrentRow"
:row-key="getRowKey"
:row-class-name="rowClassName"
:row-style="rowStyle"
:show-header="showHeader"
:show-summary="showSummary"
:summary-method="summaryMethod"
@ -97,6 +98,9 @@ const props = defineProps({
rowClassName: {
type: Function,
},
rowStyle: {
type: Function,
},
summaryMethod: {
type: Function,
},

View File

@ -1,33 +0,0 @@
<template>
<el-cascader
v-model="modelValue"
:options="options"
:props="{
multiple: true,
value: 'id',
children: 'nodes',
label: 'name',
}"
/>
</template>
<script setup>
import { layoutFnGetTrainingPlateType } from "@/assets/js/data_dictionary.js";
import { useVModel } from "@vueuse/core";
defineOptions({
name: "LayoutTrainingSectionCascader",
});
const props = defineProps({
modelValue: {
type: [String, Array],
required: true,
default: "",
},
});
const emits = defineEmits(["update:modelValue"]);
const modelValue = useVModel(props, "modelValue", emits);
const options = await layoutFnGetTrainingPlateType();
</script>
<style scoped></style>

View File

@ -59,8 +59,8 @@ axios.interceptors.response.use(
import.meta.env.DEV &&
ElMessage.error(`连接错误${error.response.status}`);
endLoading();
ElMessage.error("登录失效,请重新登陆");
router.push("/login").then();
// ElMessage.error("登录失效,请重新登陆");
// router.push("/login").then();
}
} else {
error.message = "连接到服务器失败";

View File

@ -1,9 +1,7 @@
import { post } from "@/request/axios.js";
import { useUserStore } from "@/pinia/user.js";
import pinia from "@/pinia/index.js";
import {
layoutFnGetEveryProvinceHiddenDangerType
} from "@/assets/js/data_dictionary.js";
import { layoutFnGetEveryProvinceHiddenDangerType } from "@/assets/js/data_dictionary.js";
// 获取数据字典
export const getLevels = (params) =>
@ -133,3 +131,27 @@ export const getStandardLevels = () =>
post("/hiddenstandardDictionary/list", {
loading: false,
});
// 培训类型
export const getTrainingType = (params) =>
post("/trainingtype/privateList", {
loading: false,
...params,
});
// 行业类型
export const getIndustryType = (params) =>
post("/industrytype/getIndustryTree", {
loading: false,
...params,
});
// 岗位类型
export const getPostType = (params) =>
post("/posttype/listAll", {
loading: false,
...params,
});
// 培训级别
export const getTrainingLevel = (params) =>
post("/trainleveltype/listAll", {
loading: false,
...params,
});

View File

@ -0,0 +1,4 @@
import { post } from "@/request/axios.js";
export const setExamPaperManagementDelete = (params) =>
post("/stageexampaperinput/delete", params); // 试卷管理删除

View File

@ -0,0 +1,28 @@
import { post, upload } from "@/request/axios.js";
export const getVideoCoursewareList = (params) =>
post("/platform/videocourseware/list", params); // 视频课件列表
export const getPreviewingVideo = (params) =>
post("/platform/videocourseware/getPlayInfo", params); // 预览视频
export const getVideoCoursewareView = (params) =>
post("/platform/videocourseware/goEdit", params); // 视频课件查看
export const getVideoCoursewareExercisesList = (params) =>
post("/platform/question/list", params); // 视频课件习题列表
export const getCourseManagementList = (params) =>
post("/platform/curriculum/list", params); // 课程管理列表
export const setCourseManagementDelete = (params) =>
post("/curriculum/delById", params); // 课程管理删除
export const getCourseManagementView = (params) =>
post("/curriculum/goEdit", params); // 课程管理查看
export const getSelectCourseManagementList = (params) =>
post("/videocourseware/list", params); // 选择视频课件列表
export const setCourseManagementAdd = (params) =>
upload("/curriculum/add", params); // 课程管理添加
export const setCourseManagementEdit = (params) =>
upload("/curriculum/edit", params); // 课程管理修改
export const getExamPaperManagementList = (params) =>
post("/stageexampaperinput/list", params); // 试卷管理列表
export const getExamPaperManagementView = (params) =>
post("/stageexampaperinput/goEdit", params); // 试卷管理查看
export const getExamPaperManagementTestQuestions = (params) =>
post("/paperQuestion/listForInherit", params); // 试卷管理试题

View File

@ -0,0 +1,7 @@
<template>
<layout-card>11</layout-card>
</template>
<script setup></script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,199 @@
<template>
<div>
<el-card>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPaginationTransfer"
>
<el-row>
<el-col :span="5">
<el-form-item label="试卷名称" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="创建时间" prop="dates">
<el-date-picker
v-model="searchForm.dates"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="试卷类型" prop="SOURCETYPE">
<el-select v-model="searchForm.SOURCETYPE">
<el-option
v-for="item in sourceTypeList"
:key="item.ID"
:label="item.NAME"
:value="item.ID"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="合格分数" prop="PASSSCORE">
<el-input v-model="searchForm.PASSSCORE" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPaginationTransfer">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<layout-card>
<layout-table
v-model:pagination="pagination"
:data="list"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="60">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
label="试卷名称"
prop="EXAMNAME"
show-overflow-tooltip
/>
<el-table-column label="上传时间" prop="CREATTIME" width="150" />
<el-table-column label="满分" prop="EXAMSCORE" width="150" />
<el-table-column label="合格分数" prop="PASSSCORE" width="150" />
<el-table-column label="试卷类型" width="150">
<template #default="{ row }">
{{ translationStatus(row.SOURCETYPE, sourceTypeList) }}
</template>
</el-table-column>
<el-table-column label="操作" width="200">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="
router.push({
path: '/training_process_management/exam_paper_management/view',
query: { STAGEEXAMPAPERINPUT_ID: row.STAGEEXAMPAPERINPUT_ID },
})
"
>
查看
</el-button>
<el-button
v-if="row.SOURCETYPE === '1' || row.PAPERUSERCOUNT > 0"
type="primary"
text
link
@click="
router.push({
path: '/training_process_management/exam_paper_management/edit',
query: {
STAGEEXAMPAPERINPUT_ID: row.STAGEEXAMPAPERINPUT_ID,
type: 'edit',
},
})
"
>
编辑
</el-button>
<el-button
type="primary"
text
link
@click="
router.push({
path: '/training_process_management/exam_paper_management/inherit',
query: {
STAGEEXAMPAPERINPUT_ID: row.STAGEEXAMPAPERINPUT_ID,
type: 'inherit',
},
})
"
>
继承
</el-button>
<el-button
v-show="row.SOURCETYPE === '1' || row.PAPERUSERCOUNT > 0"
type="primary"
text
link
@click="fnDelete(row.STAGEEXAMPAPERINPUT_ID)"
>
删除
</el-button>
</template>
</el-table-column>
<template #button>
<el-button
type="primary"
@click="
router.push({
path: '/training_process_management/exam_paper_management/add',
query: { type: 'add' },
})
"
>
新建试卷
</el-button>
</template>
</layout-table>
</layout-card>
</div>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import { getExamPaperManagementList } from "@/request/training_resource_management.js";
import { serialNumber, translationStatus } from "@/assets/js/utils.js";
import { useRouter } from "vue-router";
import { debounce } from "throttle-debounce";
import { ElMessage, ElMessageBox } from "element-plus";
import { setExamPaperManagementDelete } from "@/request/training_process_management.js";
const sourceTypeList = [
{ ID: "1", NAME: "平台试卷" },
{ ID: "2", NAME: "自建试卷" },
];
const router = useRouter();
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
useListData(getExamPaperManagementList);
const fnGetDataTransfer = () => {
fnGetData({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnDelete = debounce(
1000,
async (STAGEEXAMPAPERINPUT_ID) => {
await ElMessageBox.confirm("确定要删除吗?", {
type: "warning",
});
await setExamPaperManagementDelete({ STAGEEXAMPAPERINPUT_ID });
ElMessage.success("删除成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,9 @@
<template>
<view-info />
</template>
<script setup>
import ViewInfo from "@/views/training_resource_management/exam_paper_management/view.vue";
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,628 @@
<template>
<layout-card>
<el-divider content-position="left">课程基本信息</el-divider>
<el-form
ref="formRef"
:model="data.form"
:rules="rules"
label-width="100px"
>
<el-row>
<el-col :span="24">
<el-form-item label="课程名称" prop="CURRICULUMNAME">
<el-input v-model="data.form.CURRICULUMNAME" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="培训类型" prop="TRAINTYPE">
<layout-learning-train-type
ref="trainingTypeRef"
v-model="data.form.TRAINTYPE"
type="trainingType"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="行业类型" prop="INDUSTRY_LAST">
<layout-learning-train-type
ref="industryTypeRef"
v-model="data.form.INDUSTRY_LAST"
type="industryType"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="岗位类型" prop="POSTTYPE">
<layout-learning-train-type
ref="postTypeRef"
v-model="data.form.POSTTYPE"
type="postType"
:search-value="data.form.TRAINTYPE"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="培训级别" prop="TRAINLEVEL">
<layout-learning-train-type
ref="trainingLevelRef"
v-model="data.form.TRAINLEVEL"
type="trainingLevel"
:search-value="data.form.POSTTYPE"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="课程描述" prop="CURRICULUMINTRODUCE">
<el-input
v-model="data.form.CURRICULUMINTRODUCE"
type="textarea"
:autosize="{ minRows: 3 }"
/>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item label="课程封面" prop="files">
<layout-upload
v-model:file-list="data.form.files"
accept=".jpg,.jpeg,.png"
list-type="picture-card"
/>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-divider content-position="left">课程目录</el-divider>
<div v-if="!CURRICULUM_ID">
<div class="mb-10">
<el-button
:type="data.form.CATALOGUELEVEL === '1' ? 'success' : 'primary'"
@click="fnChangeCatLevel('1')"
>
一级目录样式
</el-button>
<el-button
:type="data.form.CATALOGUELEVEL === '2' ? 'success' : 'primary'"
@click="fnChangeCatLevel('2')"
>
二级目录样式
</el-button>
</div>
<div class="course_directory">
<div
v-for="(item, index) in data.chapterList"
:key="item.no"
class="course_item"
>
<div class="course_row">
<div class="course_icon">
<el-icon
v-if="
item.nodes &&
item.nodes.length !== 0 &&
item.iconDirection === '1'
"
>
<caret-bottom />
</el-icon>
</div>
<div class="course_number">{{ index + 1 }}</div>
<div class="course_play">
<el-icon v-if="item.VIDEOCOURSEWARE_ID" size="18">
<video-camera />
</el-icon>
</div>
<div class="course_input">
<el-input
v-if="item.showNameInput"
id="firstInput"
v-model="item.NAME"
@blur="fnInputBlur(item, $event)"
/>
<template v-else>{{ item.NAME }}</template>
</div>
<div>
<el-button
type="primary"
@click="fnDeleteChapter(index, item, 'first')"
>
删除
</el-button>
</div>
<div>
<el-button
type="primary"
:disabled="item.showNameInput"
@click="fnRename(item, 'firstInput')"
>
重命名
</el-button>
</div>
<div v-if="data.form.CATALOGUELEVEL === '2'">
<el-button
type="primary"
:disabled="item.showNameInput"
@click="fnAddSecondChapter(item)"
>
新增二级项
</el-button>
</div>
<div v-if="data.form.CATALOGUELEVEL === '1'">
<el-button
type="primary"
:disabled="item.showNameInput"
@click="fnAddCourseware(index, '', 'radio')"
>
{{ item.VIDEOCOURSEWARE_ID ? "更改课件" : "添加课件" }}
</el-button>
</div>
<div v-if="item.VIDEOCOURSEWARE_ID">
<el-button
type="primary"
@click="fnPreview(item.VIDEOCOURSEWARE_ID, '')"
>
预览
</el-button>
</div>
</div>
<div
v-for="(item1, index1) in item.nodes"
:key="item1.no"
class="course_row ml-30"
>
<div class="course_icon" />
<div class="course_number">{{ index + 1 }}-{{ index1 + 1 }}</div>
<div class="course_play">
<el-icon v-if="item1.VIDEOCOURSEWARE_ID" size="18">
<video-camera />
</el-icon>
</div>
<div class="course_input">
<el-input
v-if="item1.showNameInput"
id="secondInput"
v-model="item1.NAME"
@blur="fnInputBlur(item1, $event)"
/>
<template v-else>{{ item1.NAME }}</template>
</div>
<div>
<el-button
type="primary"
@click="fnDeleteChapter(index1, item, 'second')"
>
删除
</el-button>
</div>
<div>
<el-button
type="primary"
:disabled="item1.showNameInput"
@click="fnRename(item, 'secondInput')"
>
重命名
</el-button>
</div>
<div>
<el-button
type="primary"
:disabled="item1.showNameInput"
@click="fnAddCourseware(index, index1, 'radio')"
>
{{ item1.VIDEOCOURSEWARE_ID ? "更改课件" : "添加课件" }}
</el-button>
</div>
<div v-if="item1.VIDEOCOURSEWARE_ID">
<el-button
type="primary"
@click="fnPreview(item1.VIDEOCOURSEWARE_ID, '')"
>
预览
</el-button>
</div>
</div>
</div>
</div>
<div class="mt-10">
<el-button
:disabled="data.addFirstChapterFlag"
type="primary"
@click="fnAddFirstChapter"
>
新增一级项
</el-button>
<el-button
:disabled="data.addFirstChapterFlag"
type="primary"
@click="fnAddCourseware('', '', 'multiple')"
>
批量添加课件
</el-button>
</div>
</div>
<el-tree
v-if="!!CURRICULUM_ID"
:data="data.chapterList"
:props="{ children: 'nodes', label: 'COURSEWARENAME' }"
class="directory_style"
default-expand-all
>
<template #default="{ data: item }">
<div class="directory_row">
<el-icon
v-if="item.COURSEWARENAME"
class="el-icon-video-camera"
size="18"
>
<video-camera />
</el-icon>
<span class="directory_name">
{{ item.NAME || item.COURSEWARENAME }}
</span>
<span v-if="item.COURSEWARENAME" class="directory_type">
课件时长{{ secondConversion(item.VIDEOTIME) }}
</span>
<el-button
v-if="item.COURSEWARENAME"
type="primary"
size="small"
@click="fnPreview(item.VIDEOCOURSEWARE_ID, item.VIDEOFILES)"
>
预览
</el-button>
</div>
</template>
</el-tree>
<div class="mt-10 tc">
<el-button type="primary" @click="fnSubmit"></el-button>
</div>
<layout-video
v-model:visible="data.videoDialog.visible"
:src="data.videoDialog.src"
/>
<template v-if="!CURRICULUM_ID">
<add-courseware
v-model:visible="data.addCoursewareDialog.visible"
:chapter-ids="data.addCoursewareDialog.chapterIds"
:type="data.addCoursewareDialog.type"
:chapter-list="data.chapterList"
:catalogue-level="data.form.CATALOGUELEVEL"
@confirm="fnAddCoursewareConfirm"
/>
</template>
</layout-card>
</template>
<script setup>
import { useRoute, useRouter } from "vue-router";
import {
getCourseManagementView,
getPreviewingVideo,
setCourseManagementAdd,
setCourseManagementEdit,
} from "@/request/training_resource_management.js";
import { nextTick, reactive, ref } from "vue";
import { secondConversion } from "@/assets/js/utils.js";
import { CaretBottom, VideoCamera } from "@element-plus/icons-vue";
import LayoutVideo from "@/components/video/index.vue";
import LayoutUpload from "@/components/upload/index.vue";
import LayoutLearningTrainType from "@/components/learning_train_type/index.vue";
import { debounce } from "throttle-debounce";
import useFormValidate from "@/assets/js/useFormValidate.js";
import { ElMessage, ElMessageBox } from "element-plus";
import AddCourseware from "./components/add_courseware.vue";
const route = useRoute();
const router = useRouter();
const { CURRICULUM_ID } = route.query;
const rules = {
CURRICULUMNAME: [
{ required: true, message: "请输入课程名称", trigger: "blur" },
],
TRAINTYPE: [{ required: true, message: "请选择培训类型", trigger: "change" }],
INDUSTRY_LAST: [
{ required: true, message: "请选择行业类型", trigger: "change" },
],
POSTTYPE: [{ required: true, message: "请选择岗位类型", trigger: "change" }],
files: [
{
required: true,
message: "请上传封面",
trigger: "change",
},
],
CURRICULUMINTRODUCE: [
{ required: true, message: "请输入课程描述", trigger: "blur" },
],
};
const formRef = ref(null);
const industryTypeRef = ref(null);
const trainingTypeRef = ref(null);
const postTypeRef = ref(null);
const trainingLevelRef = ref(null);
const data = reactive({
form: {
CURRICULUMNAME: "",
TRAINTYPE: "",
INDUSTRY_LAST: "",
POSTTYPE: "",
TRAINLEVEL: "",
CURRICULUMINTRODUCE: "",
files: [],
CATALOGUELEVEL: "1",
COLLECTIVE: 1,
ISSELL: "1",
},
chapterList: [],
addFirstChapterFlag: false,
videoDialog: {
visible: false,
src: "",
},
addCoursewareDialog: {
chapterIds: [],
visible: false,
index: "",
index1: "",
type: "",
},
});
const fnGetData = async () => {
if (!CURRICULUM_ID) return;
const resData = await getCourseManagementView({ CURRICULUM_ID });
data.form = resData.pd;
data.chapterList = [...resData.chapterList];
};
fnGetData();
const fnPreview = async (VIDEOCOURSEWARE_ID, VIDEOFILES) => {
const resData = await getPreviewingVideo({
VIDEOCOURSEWARE_ID,
VIDEOFILES,
CURRICULUM_ID,
});
data.videoDialog.src = JSON.stringify(resData.urlList);
data.videoDialog.visible = true;
};
const fnAddFirstChapter = () => {
const firstChapter = {
VIDEOCOURSEWARE_ID: "",
NAME: "",
showNameInput: true,
SORT: data.chapterList.length + 1,
iconDirection: "1",
nodes: [],
no: Math.random(),
};
data.addFirstChapterFlag = true;
data.chapterList.push(firstChapter);
};
const fnChangeCatLevel = async (level) => {
if (data.form.CATALOGUELEVEL === level) return;
if (data.chapterList.length > 0) {
await ElMessageBox.confirm("切换目录样式后,将清空目录内容,是否继续?", {
type: "warning",
});
}
data.addFirstChapterFlag = false;
data.form.CATALOGUELEVEL = level;
data.chapterList = [];
};
const fnInputBlur = (item, event) => {
if (item.NAME) {
data.addFirstChapterFlag = false;
item.showNameInput = false;
} else {
ElMessage.warning("请输入名称");
event.target.focus();
}
};
const fnRename = async (item, element) => {
item.showNameInput = true;
await nextTick();
document.getElementById(element).focus();
};
const fnDeleteChapter = async (index, item, whichNumber) => {
await ElMessageBox.confirm(
whichNumber === "first"
? "你确定删除该一级项及下面所有二级项内容?"
: "你确定删除该二级项及关联的所有课件和试卷?",
{
type: "warning",
}
);
if (whichNumber === "first") {
data.chapterList.splice(index, 1);
} else {
item.nodes.splice(index, 1);
}
data.addFirstChapterFlag = false;
};
const fnAddSecondChapter = (item) => {
const secondChapter = {
VIDEOCOURSEWARE_ID: "",
NAME: "",
showNameInput: true,
SORT: item.nodes.length + 1,
nodes: [],
no: Math.random(),
};
item.nodes.push(secondChapter);
};
const fnAddCourseware = (index, index1, type) => {
data.addCoursewareDialog.visible = true;
data.addCoursewareDialog.index = index;
data.addCoursewareDialog.index1 = index1;
data.addCoursewareDialog.type = type;
data.addCoursewareDialog.chapterIds = [];
for (let i = 0; i < data.chapterList.length; i++) {
const chapterListItem = data.chapterList[i];
if (data.form.CATALOGUELEVEL === "1") {
if (chapterListItem.VIDEOCOURSEWARE_ID) {
data.addCoursewareDialog.chapterIds.push(
chapterListItem.VIDEOCOURSEWARE_ID
);
}
} else {
for (let j = 0; j < chapterListItem.nodes.length; j++) {
if (chapterListItem.nodes[j].VIDEOCOURSEWARE_ID) {
data.addCoursewareDialog.chapterIds.push(
chapterListItem.nodes[j].VIDEOCOURSEWARE_ID
);
}
}
}
}
};
const fnAddCoursewareConfirm = (value) => {
const { index, index1, type } = data.addCoursewareDialog;
if (type === "radio") {
if (data.form.CATALOGUELEVEL === "1") {
data.chapterList[index].VIDEOCOURSEWARE_ID = value.VIDEOCOURSEWARE_ID;
} else {
data.chapterList[index].nodes[index1].VIDEOCOURSEWARE_ID =
value.VIDEOCOURSEWARE_ID;
}
} else {
data.chapterList = [...value];
}
};
const fnSubmit = debounce(
1000,
async () => {
await useFormValidate(formRef);
for (let i = 0; i < data.chapterList.length; i++) {
const chapterListItem = data.chapterList[i];
if (data.form.CATALOGUELEVEL === "1") {
if (!chapterListItem.VIDEOCOURSEWARE_ID) {
ElMessage.warning(`序号${i + 1}课件没空`);
return;
}
} else {
if (chapterListItem.nodes.length > 0) {
for (let j = 0; j < chapterListItem.nodes.length; j++) {
if (!chapterListItem.nodes[j].VIDEOCOURSEWARE_ID) {
ElMessage.warning(`序号${i + 1}-${j + 1}课件没空`);
return;
}
}
} else {
ElMessage.warning(`序号${i + 1}没有下级目录`);
return;
}
}
}
const { name, id } = industryTypeRef.value.getIndustryTypeCurrentCheckAll();
const form = {
...data.form,
TRAINTYPE_NAME: trainingTypeRef.value.getCurrentNode().NAME,
INDUSTRY_LAST_NAME: name.at(-1),
INDUSTRY_ALL_NAME: name.join("-"),
INDUSTRY_ALL_TYPE: id.join("-"),
POSTTYPE_NAME: postTypeRef.value.getCurrentNode().NAME,
TRAINLEVEL_NAME: trainingLevelRef.value.getCurrentNode().NAME || "",
chapterList: JSON.stringify(data.chapterList),
INDUSTRY_END_ID: data.form.INDUSTRY_LAST,
};
const formData = new FormData();
Object.keys(form).forEach((key) => {
formData.append(key, form[key]);
});
formData.delete("files");
for (let i = 0; i < data.form.files.length; i++) {
if (data.form.files[i].raw)
formData.append("FFILE", data.form.files[i].raw);
}
!CURRICULUM_ID
? await setCourseManagementAdd(formData)
: await setCourseManagementEdit(formData);
ElMessage.success("保存成功");
router.back();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss">
.directory_style {
font-size: 14px;
margin-bottom: 20px;
.el-icon-video-camera {
display: inline-block;
vertical-align: middle;
margin-right: 10px;
}
.directory_row {
.directory_name {
width: 260px;
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
vertical-align: middle;
}
.directory_type {
width: 200px;
display: inline-block;
vertical-align: middle;
}
}
}
:deep {
.el-tree-node__content {
margin-bottom: 10px;
height: auto;
}
}
.course_directory {
.course_item {
.course_row {
display: flex;
align-items: center;
padding: 3px 10px;
margin-bottom: 10px;
&:hover {
background-color: var(--el-fill-color-light);
cursor: pointer;
}
div + div {
margin-left: 10px;
}
.course_icon {
color: #c0c4cc;
font-size: 12px;
width: 12px;
height: 14px;
display: block;
}
.course_play {
color: #fff;
font-size: 16px;
width: 16px;
height: 15px;
display: block;
}
.course_number {
background-color: #0d5aa3;
font-size: 13px;
text-align: center;
padding: 4px 5px;
border-radius: 4px;
}
.course_input {
width: 300px;
font-size: 13px;
}
}
}
}
</style>

View File

@ -0,0 +1,754 @@
<template>
<el-dialog v-model="visible" title="选择课件" width="90%" :on-close="fnClose">
<el-row :gutter="24">
<el-col :span="type === 'multiple' ? 16 : 24">
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPagination"
>
<el-row>
<el-col :span="6">
<el-form-item label="课程名称" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPagination">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
<layout-table
ref="tableRef"
v-model:pagination="pagination"
row-key="VIDEOCOURSEWARE_ID"
:data="list"
:row-class-name="fnRowClassName"
@get-data="fnGetData"
>
<el-table-column
type="selection"
width="55"
:selectable="fnIsDisabled"
>
<template v-if="type === 'radio'" #default="{ row }">
<el-radio
v-model="data.radioValue"
:label="row.VIDEOCOURSEWARE_ID"
:disabled="fnIsDisabled(row)"
@change="fnRadioChange(row)"
>
<span />
</el-radio>
</template>
</el-table-column>
<el-table-column label="序号" width="60">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
prop="COURSEWARENAME"
label="课程名称"
show-overflow-tooltip
/>
<el-table-column prop="POSTTYPE_NAME" label="岗位" />
<el-table-column prop="INDUSTRY_END_NAME" label="行业类型" />
<el-table-column prop="TRAININGTYPE_NAME" label="课件类型" />
<el-table-column prop="TEACHERNAME" label="讲师" width="150" />
<el-table-column prop="CLASSHOUR" label="课件学时" width="100" />
<el-table-column prop="RELATEDTERMNAME" label="相关词条">
<template #default="{ row }">
{{ row.VIDEOCOURSEWARE_ID }}
</template>
</el-table-column>
</layout-table>
</el-col>
<el-col v-if="type === 'multiple'" :span="8">
<div>
<el-divider content-position="left">课程章节</el-divider>
<div>
<div>
<el-button
:type="data.CATALOGUELEVEL === '1' ? 'success' : 'primary'"
@click="fnChangeCatLevel('1')"
>
一级目录样式
</el-button>
<el-button
:type="data.CATALOGUELEVEL === '2' ? 'success' : 'primary'"
@click="fnChangeCatLevel('2')"
>
二级目录样式
</el-button>
</div>
<el-scrollbar
ref="scroll"
:wrap-style="[{ 'overflow-x': 'hidden' }]"
style="height: 325px; margin-bottom: 10px"
>
<div class="course_directory mt-10">
<div v-if="data.chapterList.length === 0">
暂无数据请在左侧选择数据
</div>
<div
v-for="(item, index) in data.chapterList"
:key="item.no"
class="course_item"
>
<div class="course_row">
<div class="course_icon">
<el-icon
v-if="
item.nodes &&
item.nodes.length !== 0 &&
item.iconDirection === '1'
"
>
<caret-bottom />
</el-icon>
</div>
<div class="course_number">{{ index + 1 }}</div>
<div class="course_play">
<el-icon v-if="item.VIDEOCOURSEWARE_ID" size="18">
<video-camera />
</el-icon>
</div>
<div class="course_input">
<el-input
v-if="item.showNameInput"
id="firstInput"
v-model="item.NAME"
@blur="fnInputBlur(item, $event)"
/>
<template v-else>{{ item.NAME }}</template>
</div>
<div>
<el-button
type="primary"
@click="fnDeleteChapter(index, item, 'first')"
>
删除
</el-button>
</div>
<div>
<el-button
type="primary"
:disabled="item.showNameInput"
@click="fnRename(item, 'firstInput')"
>
重命名
</el-button>
</div>
<div v-if="data.CATALOGUELEVEL === '2'">
<el-button
type="primary"
:disabled="item.showNameInput"
@click="fnSaveCourseware(index)"
>
保存课件到此章节下
</el-button>
</div>
<div v-if="item.VIDEOCOURSEWARE_ID">
<el-button
type="primary"
@click="fnPreview(item.VIDEOCOURSEWARE_ID, '')"
>
预览
</el-button>
</div>
</div>
<div
v-for="(item1, index1) in item.nodes"
:key="item1.no"
class="course_row ml-30"
>
<div class="course_icon" />
<div class="course_number">
{{ index + 1 }}-{{ index1 + 1 }}
</div>
<div class="course_play">
<el-icon v-if="item1.VIDEOCOURSEWARE_ID" size="18">
<video-camera />
</el-icon>
</div>
<div class="course_input">
<el-input
v-if="item1.showNameInput"
id="secondInput"
v-model="item1.NAME"
@blur="fnInputBlur(item1, $event)"
/>
<template v-else>{{ item1.NAME }}</template>
</div>
<div>
<el-button
type="primary"
@click="fnDeleteChapter(index1, item, 'second')"
>
删除
</el-button>
</div>
<div>
<el-button
type="primary"
:disabled="item1.showNameInput"
@click="fnRename(item, 'secondInput')"
>
重命名
</el-button>
</div>
<div v-if="item1.VIDEOCOURSEWARE_ID">
<el-button
type="primary"
@click="fnPreview(item1.VIDEOCOURSEWARE_ID, '')"
>
预览
</el-button>
</div>
</div>
</div>
</div>
</el-scrollbar>
<div style="margin-top: 5px">
<el-button
v-if="data.CATALOGUELEVEL === '1'"
:disabled="data.addFirstChapterFlag"
type="primary"
@click="fnSaveCourseware('')"
>
保存课件到此章节下
</el-button>
<el-button
v-if="data.CATALOGUELEVEL === '2'"
:disabled="data.addFirstChapterFlag"
type="primary"
@click="fnAddFirstChapter"
>
新增一级项
</el-button>
</div>
</div>
</div>
</el-col>
</el-row>
<template #footer>
<el-button @click="fnClose"></el-button>
<el-button type="primary" @click="fnConfirm"></el-button>
</template>
</el-dialog>
<layout-video
v-model:visible="data.videoDialog.visible"
:src="data.videoDialog.src"
/>
</template>
<script setup>
import { useVModels } from "@vueuse/core";
import useListData from "@/assets/js/useListData.js";
import {
getPreviewingVideo,
getSelectCourseManagementList,
} from "@/request/training_resource_management.js";
import { serialNumber } from "@/assets/js/utils.js";
import { nextTick, reactive, watch } from "vue";
import { CaretBottom, VideoCamera } from "@element-plus/icons-vue";
import LayoutVideo from "@/components/video/index.vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { debounce } from "throttle-debounce";
const props = defineProps({
visible: {
type: Boolean,
required: true,
default: false,
},
chapterIds: {
type: Array,
required: true,
default: () => [],
},
type: {
type: String,
required: true,
default: "",
},
chapterList: {
type: Array,
required: true,
default: () => [],
},
catalogueLevel: {
type: String,
required: true,
default: "",
},
});
const emits = defineEmits(["update:visible", "confirm"]);
const { visible } = useVModels(props, emits);
const { list, searchForm, pagination, fnGetData, tableRef, fnResetPagination } =
useListData(getSelectCourseManagementList, {
otherParams: {
CANUSE: "1",
STATE: "0",
ISCOMPLETE: "1",
},
immediate: false,
});
let currentValue = {};
const data = reactive({
radioValue: "",
CATALOGUELEVEL: "",
chapterList: [],
addFirstChapterFlag: false,
chapterIds: [],
videoDialog: {
visible: false,
src: "",
},
});
watch(
() => props.visible,
() => {
if (props.visible) {
list.value = [
{
COURSEWAREINTRODUCE: "有秒差",
OPERATTIME: "2024-02-27 14:16:03",
TEACHERNAME: "张迪",
VIDEOCOURSEWARE_ID: "ee87b97b7cd64508bf0228c30740c363",
ISDELETE: 0,
QUESTIONCOUNT: 0,
VIDEOFILES: "d0523eabd53771ee80441777b3ce0102",
POSTTYPE: "",
STATE: "0",
SPEAKER: "012af2449ff247e1bf8749a4f4db5b5a",
CORPINFO_ID: "",
TRAINTYPE: "",
ISCOMPLETE: "1",
COURSEWARENAME: "视频时长课件",
CLASSHOUR: 0.3,
VIDEOTIME: "785.05",
OPERATOR: "admin",
CREATOR: "admin",
SOURCE: "1",
LIMIT_TIME: "自建课件",
VIDEOCAPTURE: "",
INDUSTRY_ALL_NAME: "",
CREATTIME: "2024-02-27 14:16:03",
},
{
COURSEWAREINTRODUCE: "11",
OPERATTIME: "2024-01-18 19:40:24",
TEACHERNAME: "张迪",
VIDEOCOURSEWARE_ID: "551a8869e9c0438b9c76532ee36fe3a7",
ISDELETE: 0,
QUESTIONCOUNT: 30,
VIDEOFILES: "50646463b5f671ee9e0f16b5feac0102",
POSTTYPE: "",
STATE: "0",
SPEAKER: "012af2449ff247e1bf8749a4f4db5b5a",
CORPINFO_ID: "",
TRAINTYPE: "",
ISCOMPLETE: "1",
COURSEWARENAME: "0118-1",
CLASSHOUR: 0,
VIDEOTIME: "40.536",
OPERATOR: "admin",
CREATOR: "admin",
SOURCE: "1",
LIMIT_TIME: "自建课件",
VIDEOCAPTURE: "",
INDUSTRY_ALL_NAME: "",
CREATTIME: "2024-01-18 19:40:24",
},
{
POSTTYPE_NAME: "三级教育培训",
COURSEWAREINTRODUCE: "1111",
TEACHERNAME: "张迪",
ISDELETE: 0,
QUESTIONCOUNT: 6,
VIDEOFILES: "004e87faa93b71ee9f661777b3de0102",
POSTTYPE: "d302da12d8724faaa24e5008adab8ce8",
STATE: "0",
SPEAKER: "012af2449ff247e1bf8749a4f4db5b5a",
TRAININGTYPE_NAME: "全员培训",
INDUSTRY_END_NAME: "道路运输业",
CREATOR: "admin",
LIMIT_TIME: "自建课件",
INDUSTRY_ALL_NAME: "道路运输业",
OPERATTIME: "2024-01-02 14:55:57",
VIDEOCOURSEWARE_ID: "cd455a17ff164e369c4a0bd45f88a6e6",
CORPINFO_ID: "",
TRAINTYPE: "6128172b7a1144c98cc196044281f66c",
ISCOMPLETE: "1",
COURSEWARENAME: "51秒视频",
CLASSHOUR: 0,
VIDEOTIME: "51.714",
OPERATOR: "admin",
SOURCE: "1",
VIDEOCAPTURE: "",
CREATTIME: "2024-01-02 14:55:57",
},
{
COURSEWAREINTRODUCE:
"术语;产品分类;应急喷淋器;洗眼器;洗眼/洗脸器;复合式装置 ;补充装置;标识和说明\n",
OPERATTIME: "2023-12-21 14:13:45",
TEACHERNAME: "李娜",
VIDEOCOURSEWARE_ID: "a6e69f682a7e409b890912c2dfaf028f",
ISDELETE: 0,
QUESTIONCOUNT: 15,
VIDEOFILES: "c9b18910c7ca71ed87e734b6fdcb0102",
STATE: "0",
SPEAKER: "abc501710070444f8a6cca7f4682bcdc",
CURRICULUM_ID_REMOTE: "cf9a1f623cd74a9682243ccd9c958275",
ISCOMPLETE: "1",
COURSEWARENAME: "眼面部防护 应急喷淋和洗眼设备 第1部分技术要求",
CLASSHOUR: 1,
VIDEOTIME: "2615.89",
OPERATOR: "联安众超管",
CREATOR: "admin",
SOURCE: "2",
LIMIT_TIME: "2026-04-30",
VIDEOCOURSEWARE_ID_REMOTE: "64e06f64e20b4ed1ab3b5a0dcdae3bb1",
CREATTIME: "2023-04-18 09:08:07",
},
{
COURSEWAREINTRODUCE: "包装装潢印刷;其他印刷品印刷;出版物印刷",
OPERATTIME: "2023-12-21 10:41:55",
TEACHERNAME: "侯彩侠",
VIDEOCOURSEWARE_ID: "77d5ac0920c14f07a918e0923e68dced",
ISDELETE: 0,
QUESTIONCOUNT: 10,
VIDEOFILES: "93a42690cfa771edab9316b5feac0102",
STATE: "0",
SPEAKER: "474f287cf08d491ba3bd53a81aec4eaa",
CURRICULUM_ID_REMOTE: "8226ea7cdd9c4231be3a02a00815ef7a",
ISCOMPLETE: "1",
COURSEWARENAME: "包装装潢及其他印刷业车间级安全生产培训",
CLASSHOUR: 1.2,
VIDEOTIME: "3214.08",
OPERATOR: "admin",
CREATOR: "联安众超管",
SOURCE: "2",
LIMIT_TIME: "2026-04-30",
VIDEOCOURSEWARE_ID_REMOTE: "7a5090ef80824568a57f6aedb4e71871",
CREATTIME: "2023-05-23 08:15:09",
},
{
POSTTYPE_NAME: "职业健康管理人员",
COURSEWAREINTRODUCE: "123",
TEACHERNAME: "马博",
ISDELETE: 0,
QUESTIONCOUNT: 0,
VIDEOFILES: "33151180883f71ee828d34b6fdcb0102",
POSTTYPE: "79042228778146d9bb7f0547b48a7b14",
STATE: "0",
SPEAKER: "08122e584d53442d8f57417a8bd129c7",
TRAININGTYPE_NAME: "职业健康",
INDUSTRY_END_NAME: "公路旅客运输",
CREATOR: "admin",
LIMIT_TIME: "自建课件",
INDUSTRY_ALL_NAME: "道路运输业-公路旅客运输",
OPERATTIME: "2023-11-21 15:25:52",
VIDEOCOURSEWARE_ID: "38d87d7908d945418e89e27ec77c28ad",
CORPINFO_ID: "",
TRAINTYPE: "c70bf859512241579a8a30fc5d1ae153",
ISCOMPLETE: "1",
COURSEWARENAME: "测试视频(此视频无法播放)",
CLASSHOUR: 0,
VIDEOTIME: "5.033",
OPERATOR: "admin",
SOURCE: "1",
VIDEOCAPTURE: "",
CREATTIME: "2023-11-21 15:25:52",
},
{
COURSEWAREINTRODUCE:
"51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件",
OPERATTIME: "2023-10-17 16:58:51",
TEACHERNAME: "张迪",
VIDEOCOURSEWARE_ID: "2f8ae0ce434541848a571e5ecc0a44f8",
ISDELETE: 0,
QUESTIONCOUNT: 0,
VIDEOFILES: "63eef6706ccb71eebfd10666a2ec0102",
POSTTYPE: "",
STATE: "0",
SPEAKER: "012af2449ff247e1bf8749a4f4db5b5a",
CORPINFO_ID: "",
TRAINTYPE: "",
ISCOMPLETE: "1",
COURSEWARENAME:
"51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件51秒课件",
CLASSHOUR: 0,
VIDEOTIME: "51.714",
OPERATOR: "admin",
CREATOR: "admin",
SOURCE: "1",
LIMIT_TIME: "自建课件",
VIDEOCAPTURE: "",
INDUSTRY_ALL_NAME: "",
CREATTIME: "2023-10-17 16:58:51",
},
{
POSTTYPE_NAME: "三级教育培训",
COURSEWAREINTRODUCE: "1分30秒视频测试",
TEACHERNAME: "刘敬英",
ISDELETE: 0,
QUESTIONCOUNT: 0,
VIDEOFILES: "aa2664b0670e71ee825d1777b3ce0102",
POSTTYPE: "d302da12d8724faaa24e5008adab8ce8",
STATE: "0",
SPEAKER: "01ea51f0b07a402b8a7785111bc79be7",
TRAININGTYPE_NAME: "全员培训",
INDUSTRY_END_NAME: "道路运输业",
CREATOR: "admin",
LIMIT_TIME: "自建课件",
INDUSTRY_ALL_NAME: "道路运输业",
OPERATTIME: "2023-10-17 15:24:36",
VIDEOCOURSEWARE_ID: "19f3f065b67e4242906155a3eafc86ef",
CORPINFO_ID: "",
TRAINTYPE: "6128172b7a1144c98cc196044281f66c",
ISCOMPLETE: "1",
COURSEWARENAME: "1分30秒视频测试",
CLASSHOUR: 0,
VIDEOTIME: "88.542",
OPERATOR: "admin",
SOURCE: "1",
VIDEOCAPTURE: "",
CREATTIME: "2023-10-10 09:45:20",
},
{
COURSEWAREINTRODUCE: "30秒视频",
OPERATTIME: "2023-10-10 09:47:34",
TEACHERNAME: "高静",
VIDEOCOURSEWARE_ID: "5ec4aeec7178426caaac1cd5c4165858",
ISDELETE: 0,
QUESTIONCOUNT: 0,
VIDEOFILES: "fa09e6a0670e71eebc35752281fd0102",
POSTTYPE: "",
STATE: "0",
SPEAKER: "0a2324c8ffe74152b7ed2bbdb5c35dda",
CORPINFO_ID: "",
TRAINTYPE: "",
ISCOMPLETE: "1",
COURSEWARENAME: "30秒视频",
CLASSHOUR: 0,
VIDEOTIME: "31.0",
OPERATOR: "admin",
CREATOR: "admin",
SOURCE: "1",
LIMIT_TIME: "自建课件",
VIDEOCAPTURE: "",
INDUSTRY_ALL_NAME: "",
CREATTIME: "2023-10-10 09:47:34",
},
{
COURSEWAREINTRODUCE: "1分钟视频",
TEACHERNAME: "张迪",
ISDELETE: 0,
QUESTIONCOUNT: 0,
VIDEOFILES: "0513fe70664f71ee834c1776b3ce0102",
POSTTYPE: "",
STATE: "0",
SPEAKER: "012af2449ff247e1bf8749a4f4db5b5a",
TRAININGTYPE_NAME: "全员培训",
INDUSTRY_END_NAME: "选矿厂",
CREATOR: "admin",
LIMIT_TIME: "自建课件",
INDUSTRY_ALL_NAME: "高危行业-金属非金属矿山-选矿厂",
OPERATTIME: "2023-10-09 10:53:28",
VIDEOCOURSEWARE_ID: "5c097d1b383042728eb99fb6e5c982d3",
CORPINFO_ID: "",
TRAINTYPE: "6128172b7a1144c98cc196044281f66c",
ISCOMPLETE: "1",
COURSEWARENAME: "1分钟课件",
CLASSHOUR: 0,
VIDEOTIME: "54.547",
OPERATOR: "admin",
SOURCE: "1",
VIDEOCAPTURE: "",
CREATTIME: "2023-10-09 10:53:28",
},
];
data.CATALOGUELEVEL = props.catalogueLevel;
data.chapterList = [...props.chapterList];
data.chapterIds = [...props.chapterIds];
}
}
);
const fnRadioChange = (row) => {
currentValue = row;
};
const fnRowClassName = ({ row }) => {
return data.chapterIds.some((item) => item === row.VIDEOCOURSEWARE_ID)
? "text-red"
: "";
};
const fnIsDisabled = (row) => {
if (props.type === "radio")
return data.chapterIds.some((item) => item === row.VIDEOCOURSEWARE_ID);
else return !data.chapterIds.some((item) => item === row.VIDEOCOURSEWARE_ID);
};
const fnPreview = async (VIDEOCOURSEWARE_ID, VIDEOFILES) => {
const resData = await getPreviewingVideo({
VIDEOCOURSEWARE_ID,
VIDEOFILES,
});
data.videoDialog.src = JSON.stringify(resData.urlList);
data.videoDialog.visible = true;
};
const fnChangeCatLevel = async (level) => {
if (data.CATALOGUELEVEL === level) return;
if (data.chapterList.length > 0) {
await ElMessageBox.confirm("切换目录样式后,将清空目录内容,是否继续?", {
type: "warning",
});
}
data.addFirstChapterFlag = false;
data.CATALOGUELEVEL = level;
data.chapterList = [];
data.chapterIds = [];
};
const fnInputBlur = (item, event) => {
if (item.NAME) {
data.addFirstChapterFlag = false;
item.showNameInput = false;
} else {
ElMessage.warning("请输入名称");
event.target.focus();
}
};
const fnRename = async (item, element) => {
item.showNameInput = true;
await nextTick();
document.getElementById(element).focus();
};
const fnDeleteChapter = async (index, item, whichNumber) => {
await ElMessageBox.confirm(
whichNumber === "first"
? "你确定删除该一级项及下面所有二级项内容?"
: "你确定删除该二级项及关联的所有课件和试卷?",
{
type: "warning",
}
);
if (whichNumber === "first") {
data.chapterList.splice(index, 1);
} else {
item.nodes.splice(index, 1);
}
data.addFirstChapterFlag = false;
};
const fnAddFirstChapter = () => {
const firstChapter = {
VIDEOCOURSEWARE_ID: "",
NAME: "",
showNameInput: true,
SORT: data.chapterList.length + 1,
iconDirection: "1",
nodes: [],
no: Math.random(),
};
data.addFirstChapterFlag = true;
data.chapterList.push(firstChapter);
};
const fnSaveCourseware = (index) => {
const selectionData = tableRef.value.getSelectionRows();
if (selectionData.length === 0) {
ElMessage.warning("请选择要添加的课件");
return;
}
for (let i = 0; i < selectionData.length; i++) {
selectionData[i].NAME = selectionData[i].COURSEWARENAME;
data.chapterIds.push(selectionData[i].VIDEOCOURSEWARE_ID);
}
if (data.CATALOGUELEVEL === "1") {
data.chapterList = [...data.chapterList, ...selectionData];
} else {
data.chapterList[index].nodes = [
...data.chapterList[index].nodes,
...selectionData,
];
}
ElMessage.success("添加成功");
tableRef.value.clearSelection();
};
const fnClose = () => {
data.radioValue = "";
data.CATALOGUELEVEL = "";
data.chapterList = [];
data.addFirstChapterFlag = false;
data.chapterIds = [];
currentValue = {};
searchForm.value.KEYWORDS = "";
visible.value = false;
tableRef.value.clearSelection();
};
const fnConfirm = debounce(
1000,
() => {
const value = props.type === "radio" ? currentValue : data.chapterList;
emits("confirm", value);
fnClose();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss">
.course_directory {
.course_item {
.course_row {
display: flex;
align-items: center;
padding: 3px 10px;
margin-bottom: 10px;
&:hover {
background-color: var(--el-fill-color-light);
cursor: pointer;
}
div + div {
margin-left: 10px;
}
.course_icon {
color: #c0c4cc;
font-size: 12px;
width: 12px;
height: 14px;
display: block;
}
.course_play {
color: #fff;
font-size: 16px;
width: 16px;
height: 15px;
display: block;
}
.course_number {
width: 100px;
background-color: #0d5aa3;
font-size: 13px;
text-align: center;
padding: 4px 5px;
border-radius: 4px;
}
.course_input {
width: 300px;
font-size: 13px;
}
}
}
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<div>
<el-card>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPaginationTransfer"
>
<el-row>
<el-col :span="6">
<el-form-item label="课程名称" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="创建时间" prop="dates">
<el-date-picker
v-model="searchForm.dates"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="培训类型" prop="TRAINTYPE">
<layout-learning-train-type
v-model="searchForm.TRAINTYPE"
type="trainingType"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="行业类型" prop="INDUSTRY_END_ID">
<layout-learning-train-type
v-model="searchForm.INDUSTRY_END_ID"
type="industryType"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="岗位类型" prop="POSTTYPE">
<layout-learning-train-type
v-model="searchForm.POSTTYPE"
type="postType"
:search-value="searchForm.TRAINTYPE"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="培训级别" prop="TRAINLEVEL">
<layout-learning-train-type
v-model="searchForm.TRAINLEVEL"
type="trainingLevel"
:search-value="searchForm.TRAINTYPE"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPaginationTransfer">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<layout-card>
<layout-table
v-model:pagination="pagination"
:data="list"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="60">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
label="课程名称"
prop="CURRICULUMNAME"
show-overflow-tooltip
/>
<el-table-column label="上传时间" prop="CREATTIME" width="150" />
<el-table-column label="培训类型" prop="TRAININGTYPE_NAME" />
<el-table-column label="行业类型">
<template #default="{ row }">
{{ row.INDUSTRY_END_NAME || row.INDUSTRY_ALL_NAME }}
</template>
</el-table-column>
<el-table-column label="岗位类型" prop="POSTTYPE_NAME" />
<el-table-column label="课件数" prop="COURSEWARECOUNT" width="100" />
<el-table-column label="课件时长" width="100">
<template #default="{ row }">
{{ secondConversion(row.VIDEOTIME) }}
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="
router.push({
path: '/training_resource_management/course_management/view',
query: { CURRICULUM_ID: row.CURRICULUM_ID },
})
"
>
查看
</el-button>
<el-button
v-if="row.IS_CORP_CREATE === 1"
type="primary"
text
link
@click="
router.push({
path: '/training_resource_management/course_management/add',
query: { CURRICULUM_ID: row.CURRICULUM_ID },
})
"
>
编辑
</el-button>
<el-button
v-if="row.IS_CORP_CREATE === 1"
type="primary"
text
link
@click="fnDelete(row.CURRICULUM_ID)"
>
删除
</el-button>
</template>
</el-table-column>
<template #button>
<el-button
type="primary"
@click="
router.push({
path: '/training_resource_management/course_management/add',
})
"
>
新增
</el-button>
</template>
</layout-table>
</layout-card>
</div>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import {
getCourseManagementList,
setCourseManagementDelete,
} from "@/request/training_resource_management.js";
import LayoutLearningTrainType from "@/components/learning_train_type/index.vue";
import { secondConversion, serialNumber } from "@/assets/js/utils.js";
import { useRouter } from "vue-router";
import { debounce } from "throttle-debounce";
import { ElMessage, ElMessageBox } from "element-plus";
const router = useRouter();
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
useListData(getCourseManagementList);
const fnGetDataTransfer = () => {
fnGetData({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnDelete = debounce(
1000,
async (CURRICULUM_ID) => {
await ElMessageBox.confirm("确定要删除吗?", {
type: "warning",
});
await setCourseManagementDelete({ CURRICULUM_ID });
ElMessage.success("删除成功");
fnResetPaginationTransfer();
},
{ atBegin: true }
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,137 @@
<template>
<layout-card>
<el-divider content-position="left">课程基本信息</el-divider>
<el-descriptions :column="1" border>
<el-descriptions-item label="课程名称">
{{ data.info.CURRICULUMNAME }}
</el-descriptions-item>
<el-descriptions-item label="培训类型">
{{ data.info.TYPENAME }}
</el-descriptions-item>
<el-descriptions-item label="课程描述">
{{ data.info.CURRICULUMINTRODUCE }}
</el-descriptions-item>
<el-descriptions-item label="课程封面">
<img
v-viewer
:src="VITE_FILE_URL + data.info.COVERPATH"
alt=""
width="100"
height="100"
/>
</el-descriptions-item>
</el-descriptions>
<el-divider content-position="left">课程目录</el-divider>
<el-tree
:data="data.chapterList"
:props="{ children: 'nodes', label: 'COURSEWARENAME' }"
class="directory_style"
default-expand-all
>
<template #default="{ data: item }">
<div class="directory_row">
<el-icon
v-if="item.COURSEWARENAME"
class="el-icon-video-camera"
size="18"
>
<video-camera />
</el-icon>
<span class="directory_name">
{{ item.NAME || item.COURSEWARENAME }}
</span>
<span v-if="item.COURSEWARENAME" class="directory_type">
课件时长{{ secondConversion(item.VIDEOTIME) }}
</span>
<el-button
v-if="item.COURSEWARENAME"
type="primary"
size="small"
@click="fnPreview(item.VIDEOCOURSEWARE_ID, item.VIDEOFILES)"
>
预览
</el-button>
</div>
</template>
</el-tree>
<layout-video
v-model:visible="data.videoDialog.visible"
:src="data.videoDialog.src"
/>
</layout-card>
</template>
<script setup>
import { useRoute } from "vue-router";
import {
getCourseManagementView,
getPreviewingVideo,
} from "@/request/training_resource_management.js";
import { reactive } from "vue";
import { secondConversion } from "@/assets/js/utils.js";
import { VideoCamera } from "@element-plus/icons-vue";
import LayoutVideo from "@/components/video/index.vue";
const route = useRoute();
const { CURRICULUM_ID } = route.query;
const VITE_FILE_URL = import.meta.env.VITE_FILE_URL;
const data = reactive({
info: {},
chapterList: [],
videoDialog: {
visible: false,
src: "",
},
});
const fnGetData = async () => {
const resData = await getCourseManagementView({ CURRICULUM_ID });
data.info = resData.pd;
data.chapterList = resData.chapterList;
};
fnGetData();
const fnPreview = async (VIDEOCOURSEWARE_ID, VIDEOFILES) => {
const resData = await getPreviewingVideo({
VIDEOCOURSEWARE_ID,
VIDEOFILES,
CURRICULUM_ID,
});
data.videoDialog.src = JSON.stringify(resData.urlList);
data.videoDialog.visible = true;
};
</script>
<style scoped lang="scss">
.directory_style {
font-size: 14px;
margin-bottom: 20px;
.el-icon-video-camera {
display: inline-block;
vertical-align: middle;
margin-right: 10px;
}
.directory_row {
.directory_name {
width: 260px;
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
vertical-align: middle;
}
.directory_type {
width: 200px;
display: inline-block;
vertical-align: middle;
}
}
}
:deep {
.el-tree-node__content {
margin-bottom: 10px;
height: auto;
}
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<div>
<el-card>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPaginationTransfer"
>
<el-row>
<el-col :span="5">
<el-form-item label="试卷名称" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="创建时间" prop="dates">
<el-date-picker
v-model="searchForm.dates"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="满分" prop="EXAMSCORE">
<el-input v-model="searchForm.EXAMSCORE" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="合格分数" prop="PASSSCORE">
<el-input v-model="searchForm.PASSSCORE" />
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPaginationTransfer">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<layout-card>
<layout-table
v-model:pagination="pagination"
:data="list"
@get-data="fnGetDataTransfer"
>
<el-table-column label="序号" width="60">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
label="试卷名称"
prop="EXAMNAME"
show-overflow-tooltip
/>
<el-table-column label="上传时间" prop="CREATTIME" width="150" />
<el-table-column label="满分" prop="EXAMSCORE" width="150" />
<el-table-column label="合格分数" prop="PASSSCORE" width="150" />
<el-table-column label="试卷状态" width="150">
<template #default="{ row }">
{{ row.ISSELL === "0" ? "下架" : "上架" }}
</template>
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="
router.push({
path: '/training_resource_management/exam_paper_management/view',
query: { STAGEEXAMPAPERINPUT_ID: row.STAGEEXAMPAPERINPUT_ID },
})
"
>
查看
</el-button>
</template>
</el-table-column>
</layout-table>
</layout-card>
</div>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import { getExamPaperManagementList } from "@/request/training_resource_management.js";
import { serialNumber } from "@/assets/js/utils.js";
import { useRouter } from "vue-router";
const router = useRouter();
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
useListData(getExamPaperManagementList);
const fnGetDataTransfer = () => {
fnGetData({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
const fnResetPaginationTransfer = () => {
fnResetPagination({
STARTTIME: searchForm.value.dates?.[0],
ENDTIME: searchForm.value.dates?.[1],
});
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,109 @@
<template>
<layout-card>
<el-divider content-position="left">试卷基本信息</el-divider>
<el-descriptions :column="2" border>
<el-descriptions-item label="试卷名称" :span="2">
{{ info.EXAMNAME }}
</el-descriptions-item>
<el-descriptions-item label="合格分数">
{{ info.PASSSCORE }}
</el-descriptions-item>
<el-descriptions-item label="总分数">
{{ info.EXAMSCORE }}
</el-descriptions-item>
</el-descriptions>
<el-divider content-position="left">试卷题目信息</el-divider>
<div class="items mt-20 p-20">
<div
v-for="(item, index) in testPaper"
:key="item.PAPER_QUESTION_ID"
class="item ptb-20"
>
<div class="mt-10">
{{ index + 1 }}.
<span v-if="item.QUESTIONTYPE === '1'"> () </span>
<span v-if="item.QUESTIONTYPE === '2'"> () </span>
<span v-if="item.QUESTIONTYPE === '3'"> () </span>
{{ item.QUESTIONDRY }}
<span class="ml-10">(题目分值{{ item.SCORE }})</span>
</div>
<div class="mt-10 ml-30">
<el-radio-group
v-if="item.QUESTIONTYPE === '1'"
disabled
:model-value="item.ANSWER"
>
<el-radio label="A">A.{{ item.OPTIONA }}</el-radio>
<el-radio label="B">B.{{ item.OPTIONB }}</el-radio>
<el-radio label="C">C.{{ item.OPTIONC }}</el-radio>
<el-radio label="D">D.{{ item.OPTIOND }}</el-radio>
</el-radio-group>
<el-checkbox-group
v-if="item.QUESTIONTYPE === '2'"
disabled
:model-value="item.ANSWER?.split('')"
>
<el-checkbox label="A">A.{{ item.OPTIONA }}</el-checkbox>
<el-checkbox label="B">B.{{ item.OPTIONB }}</el-checkbox>
<el-checkbox label="C">C.{{ item.OPTIONC }}</el-checkbox>
<el-checkbox label="D">D.{{ item.OPTIOND }}</el-checkbox>
</el-checkbox-group>
<el-radio-group
v-if="item.QUESTIONTYPE === '3'"
disabled
:model-value="item.ANSWER"
>
<el-radio label="A">A.{{ item.OPTIONA }}</el-radio>
<el-radio label="B">B.{{ item.OPTIONB }}</el-radio>
</el-radio-group>
</div>
<div class="mt-10">答案{{ item.ANSWER }}</div>
<div class="mt-10">答案解析{{ item.DESCR }}</div>
<div class="mt-10">关联课件名称{{ item.COURSEWARENAME }}</div>
</div>
</div>
</layout-card>
</template>
<script setup>
import { ref } from "vue";
import {
getExamPaperManagementTestQuestions,
getExamPaperManagementView,
} from "@/request/training_resource_management.js";
import { useRoute } from "vue-router";
const route = useRoute();
const { STAGEEXAMPAPERINPUT_ID } = route.query;
const info = ref({});
const testPaper = ref([]);
const fnGetData = async () => {
const { pd } = await getExamPaperManagementView({
STAGEEXAMPAPERINPUT_ID,
});
info.value = pd;
const { varList } = await getExamPaperManagementTestQuestions({
STAGEEXAMPAPERINPUT_ID,
});
testPaper.value = varList;
};
fnGetData();
</script>
<style scoped lang="scss">
.items {
border: 1px solid var(--el-border-color);
.item {
border-bottom: 1px dashed #ebeef5;
&:first-child {
padding-top: 0;
}
&:last-child {
border-bottom: none;
}
}
}
</style>

View File

@ -0,0 +1,51 @@
<template>
<layout-card>
<layout-table
v-model:pagination="pagination"
:data="list"
@get-data="fnGetData"
>
<el-table-column label="题号" prop="QUESTIONNUMBER" width="100" />
<el-table-column label="试题类型" width="100">
<template #default="{ row }">
<el-tag v-if="row.QUESTIONTYPE === '1'" type="success"></el-tag>
<el-tag v-else-if="row.QUESTIONTYPE === '2'">多选题</el-tag>
<el-tag v-else-if="row.QUESTIONTYPE === '3'" type="warning">
判断题
</el-tag>
<el-tag v-else-if="row.QUESTIONTYPE === '4'" type="info">
填空题
</el-tag>
</template>
</el-table-column>
<el-table-column label="题干" prop="QUESTIONDRY" />
<el-table-column label="答案" width="100">
<template #default="{ row }">
<div v-if="row.QUESTIONTYPE === '3'">
<div v-if="row.ANSWER === 'A'"></div>
<div v-if="row.ANSWER === 'B'"></div>
</div>
<div v-else>{{ row.ANSWER }}</div>
</template>
</el-table-column>
<el-table-column label="答案解析" prop="DESCR" />
</layout-table>
</layout-card>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import { getVideoCoursewareExercisesList } from "@/request/training_resource_management.js";
import { useRoute } from "vue-router";
const route = useRoute();
const { COURSEWAREID, COURSEWARETYPE } = route.query;
const { list, pagination, fnGetData } = useListData(
getVideoCoursewareExercisesList,
{
otherParams: { COURSEWAREID, COURSEWARETYPE },
}
);
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,158 @@
<template>
<div>
<el-card>
<el-form
:model="searchForm"
label-width="100px"
@submit.prevent="fnResetPagination"
>
<el-row>
<el-col :span="5">
<el-form-item label="课程名称" prop="KEYWORDS">
<el-input v-model="searchForm.KEYWORDS" />
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="培训类型" prop="TRAINTYPE">
<layout-learning-train-type
v-model="searchForm.TRAINTYPE"
type="trainingType"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="行业类型" prop="INDUSTRY_END_ID">
<layout-learning-train-type
v-model="searchForm.INDUSTRY_END_ID"
type="industryType"
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item label="岗位类型" prop="POSTTYPE">
<layout-learning-train-type
v-model="searchForm.POSTTYPE"
type="postType"
:search-value="searchForm.TRAINTYPE"
/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-form-item label-width="10px">
<el-button type="primary" native-type="submit">搜索</el-button>
<el-button native-type="reset" @click="fnResetPagination">
重置
</el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-card>
<layout-card>
<layout-table
v-model:pagination="pagination"
:data="list"
@get-data="fnGetData"
>
<el-table-column label="序号" width="60">
<template #default="{ $index }">
{{ serialNumber(pagination, $index) }}
</template>
</el-table-column>
<el-table-column
label="课件名称"
prop="COURSEWARENAME"
show-overflow-tooltip
/>
<el-table-column label="上传时间" prop="CREATTIME" width="150" />
<el-table-column label="培训类型" prop="TRAININGTYPE_NAME" />
<el-table-column label="行业类型" prop="INDUSTRY_END_NAME" />
<el-table-column label="岗位类型" prop="POSTTYPE_NAME" />
<el-table-column label="培训教师" prop="TEACHERNAME" />
<el-table-column label="学时" prop="CLASSHOUR" width="100" />
<el-table-column label="课件时长" width="100">
<template #default="{ row }">
{{ secondConversion(row.VIDEOTIME) }}
</template>
</el-table-column>
<el-table-column label="相关词条" prop="RELATEDTERMNAME" />
<el-table-column label="习题数" prop="QUESTIONCOUNT" width="100" />
<el-table-column label="操作" width="180">
<template #default="{ row }">
<el-button
type="primary"
text
link
@click="fnPreview(row.VIDEOCOURSEWARE_ID, row.VIDEOFILES)"
>
预览
</el-button>
<el-button
type="primary"
text
link
@click="
router.push({
path: '/training_resource_management/video_courseware/view',
query: { VIDEOCOURSEWARE_ID: row.VIDEOCOURSEWARE_ID },
})
"
>
详情
</el-button>
<el-button
type="primary"
text
link
@click="
router.push({
path: '/training_resource_management/video_courseware/exercises',
query: {
COURSEWAREID: row.VIDEOCOURSEWARE_ID,
COURSEWARETYPE: 1,
},
})
"
>
课件习题
</el-button>
</template>
</el-table-column>
</layout-table>
</layout-card>
<layout-video
v-model:visible="data.videoDialog.visible"
:src="data.videoDialog.src"
/>
</div>
</template>
<script setup>
import useListData from "@/assets/js/useListData.js";
import {
getPreviewingVideo,
getVideoCoursewareList,
} from "@/request/training_resource_management.js";
import LayoutLearningTrainType from "@/components/learning_train_type/index.vue";
import LayoutVideo from "@/components/video/index.vue";
import { secondConversion, serialNumber } from "@/assets/js/utils.js";
import { useRouter } from "vue-router";
import { reactive } from "vue";
const router = useRouter();
const { list, pagination, searchForm, fnGetData, fnResetPagination } =
useListData(getVideoCoursewareList);
const data = reactive({
videoDialog: {
visible: false,
src: "",
},
});
const fnPreview = async (VIDEOCOURSEWARE_ID, VIDEOFILES) => {
const resData = await getPreviewingVideo({ VIDEOCOURSEWARE_ID, VIDEOFILES });
data.videoDialog.src = JSON.stringify(resData.urlList);
data.videoDialog.visible = true;
};
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,76 @@
<template>
<layout-card>
<el-divider content-position="left">课件基本信息</el-divider>
<el-descriptions :column="2" border>
<el-descriptions-item label="课件名称">
{{ data.info.COURSEWARENAME }}
</el-descriptions-item>
<el-descriptions-item label="培训类型">
{{ data.info.TYPENAME }}
</el-descriptions-item>
<el-descriptions-item label="讲师名称">
{{ data.info.SPEAKERNAME }}
</el-descriptions-item>
<el-descriptions-item label="课件文件">
{{ data.info.COURSEWARENAME }}
<el-button type="primary" size="small" class="ml-10" @click="fnPreview">
预览
</el-button>
</el-descriptions-item>
<el-descriptions-item label="课件截图">
<img
v-viewer
:src="data.info.VIDEOCAPTURE"
alt=""
width="100"
height="100"
/>
</el-descriptions-item>
<el-descriptions-item label="课件描述">
{{ data.info.COURSEWAREINTRODUCE }}
</el-descriptions-item>
<el-descriptions-item label="相关词条">
{{ data.info.RELATEDTERMNAME }}
</el-descriptions-item>
</el-descriptions>
<layout-video
v-model:visible="data.videoDialog.visible"
:src="data.videoDialog.src"
/>
</layout-card>
</template>
<script setup>
import { reactive } from "vue";
import {
getPreviewingVideo,
getVideoCoursewareView,
} from "@/request/training_resource_management.js";
import { useRoute } from "vue-router";
import LayoutVideo from "@/components/video/index.vue";
const route = useRoute();
const { VIDEOCOURSEWARE_ID } = route.query;
const data = reactive({
info: {},
videoDialog: {
visible: false,
src: "",
},
});
const fnGetData = async () => {
const resData = await getVideoCoursewareView({ VIDEOCOURSEWARE_ID });
data.info = resData.pd;
};
fnGetData();
const fnPreview = async () => {
const resData = await getPreviewingVideo({
VIDEOCOURSEWARE_ID: data.info.VIDEOCOURSEWARE_ID,
VIDEOFILES: data.info.VIDEOFILES,
});
data.videoDialog.src = JSON.stringify(resData.urlList);
data.videoDialog.visible = true;
};
</script>
<style scoped lang="scss"></style>