This commit is contained in:
小杨 2024-12-04 14:58:02 +08:00
commit 8d03c927b9
11 changed files with 849 additions and 15 deletions

View File

@ -6,6 +6,7 @@
import { toRaw } from 'vue'
import msgUtils from '@/plugins/modal' // 消息工具
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // 相关api
import * as API_smarttalk from '@/api/file' // 相关api
import * as useStore from '../store' // pptist-状态管理
import { sessionStore } from '@/utils/store' // electron-store 状态管理
import useUserStore from '@/store/modules/user' // 外部-用户信息
@ -174,6 +175,14 @@ export class PPTApi {
} else msgUtils.msgError(res.msg || '删除失败');resolve(false)
})
}
// 更新-备课资源 标题
static updateSmarttalk(data: object): Promise<Boolean> {
return API_smarttalk.updateSmarttalk(data).then(res => {
if (res.code === 200) return true
else msgUtils.msgError(res.msg || '更新失败');return false
})
}
}
export default PPTApi

View File

@ -7,7 +7,8 @@ import { PPTApi } from './index'
import * as store from '../store'
import { sessionStore } from '@/utils/store' // electron-store 状态管理
const slidesStore = store.useSlidesStore()
const resource = sessionStore.get('curr.resource')
const resource = sessionStore.get('curr.resource') // apt 资源
const smarttalk = sessionStore.get('curr.smarttalk') // 备课资源
/**
* @description
*/
@ -28,4 +29,11 @@ const updatePPT = async (data) => {
data.id = resource.id
await PPTApi.updateSlide(data) // 更新ppt内容
sessionStore.set('curr.resource.title', data.title)
// 更新smarttalk内容
if (!!smarttalk && !!data.title) {
const {id, fileFlag} = smarttalk
const params = { id, fileShowName: `${data.title}.${fileFlag}` }
await PPTApi.updateSmarttalk(params) // 更新ppt内容
sessionStore.set('curr.smarttalk.fileShowName', params.fileShowName)
}
}

View File

@ -0,0 +1,81 @@
import axios from 'axios'
import request from '@/utils/request'
import { getToken } from "@/utils/auth";
// 文生图片
export function convertTextToPicture(data) {
return axios({
url: 'https://ai.ysaix.com:7853/prompt',
method: 'post',
headers: {
'Content-Type': 'application/json',
'Accept': '*/*'
},
data: data
})
}
// 获取任务列表
export function getQueue() {
return axios({
url: `https://ai.ysaix.com:7853/queue`,
method: 'get',
})
}
// 获取生图任务id
export function getPromptId(id) {
return axios({
url: `https://ai.ysaix.com:7853/history/${id}`,
method: 'get',
})
}
// 获取生成图片路径
export function getPicture(data) {
return axios({
url: 'https://ai.ysaix.com:7853/view',
method: 'get',
params: data
})
}
// 大模型对话生成prompt模板
export function chattoprompt(dataset_id,prompt) {
return axios({
url: '/api/v1/parse/docs',
method: 'post',
headers: {
'Authorization': 'Bearer ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm',
},
data: {
'dataset_id': dataset_id,
'prompt': prompt
}
})
}
// prompt敏感词校验
export function textSensitiveWord(data) {
return request({
url: '/verify/text',
method: 'post',
data: {
'text' : data
}
})
}
// 图片上传资源库
export function uploadPicture(data) {
return axios({
url: '/dev-api/smarttalk/file/upload',
method: 'post',
headers: {
'Accept': '*/*',
'Content-Type': 'multipart/form-data',
'Authorization': "Bearer " + getToken()
},
data: data
})
}

View File

@ -0,0 +1,627 @@
<template>
<div>
<el-row :gutter="40" style="height:auto">
<el-col :span="8">
<div>
<div style="text-align: left">反面提示词</div>
<br>
<el-input v-model="formData.prompt[12].inputs.negative_prompt" placeholder="试试在这里输入你不想其进入图画的词"
type="textarea"></el-input>
</div>
<br>
<div>
<div style="text-align: left">可选提示词</div>
<br>
<div style="font-size: 14px;text-align: left">风格特点</div>
<el-checkbox-group v-model="checkList">
<el-checkbox v-for="(prompt, index) in promptOption1" :key="index" :label="prompt" :value="prompt" />
</el-checkbox-group>
</div>
<div>
<div style="font-size: 14px;text-align: left">主体对象</div>
<el-checkbox-group v-model="checkList">
<el-checkbox v-for="(prompt, index) in promptOption2" :key="index" :label="prompt" :value="prompt" />
</el-checkbox-group>
</div>
<div>
<div style="font-size: 14px;text-align: left">场景</div>
<el-checkbox-group v-model="checkList">
<el-checkbox v-for="(prompt, index) in promptOption3" :key="index" :label="prompt" :value="prompt" />
</el-checkbox-group>
</div>
<div style="text-align: right">
<el-button type="primary" @click="addCheckListToPrompt">添加</el-button>
<el-button type="primary" @click="deletePrompt">清空</el-button>
</div>
<!-- 具体参数相关内容 -->
<el-form label-position="top" size="small">
<el-form-item label="图片尺寸" style=" margin-bottom: 0px">
<el-row class="ratio-options" :gutter="10" style="margin-bottom: -15px;margin-top: -25px">
<el-col v-for="ratio in ratioOptions" :key="ratio.value" :span="4">
<el-button :type="form.ratio === ratio.value ? 'primary' : 'default'" @click="setRatio(ratio.value)"
class="ratio-option" block>
{{ ratio.label }}
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="图片比例" style=" margin-bottom: 0px">
<el-row class="ratio-options" :gutter="10" style="margin-bottom: -15px;margin-top: -25px">
<el-col v-for="ratio in ratioOptions2" :key="ratio.value" :span="4">
<el-button :type="form.ratios2 === ratio.value ? 'primary' : 'default'" @click="setRatio2(ratio.value)"
class="ratio-option" block>
{{ ratio.label }}
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="图片数量" style=" margin-bottom: 0px">
<el-row style="margin-bottom: -15px;margin-top: -25px">
<el-button style="width: 10%" :type="num_picture === 1 ? 'primary' : 'default'" @click="changePicture(1)">
1
</el-button>
<el-button style="width: 10%" :type="num_picture === 2 ? 'primary' : 'default'" @click="changePicture(2)">
2
</el-button>
<el-button style="width: 10%" :type="num_picture === 3 ? 'primary' : 'default'" @click="changePicture(3)">
3
</el-button>
<el-button style="width: 10%" :type="num_picture === 4 ? 'primary' : 'default'" @click="changePicture(4)">
4
</el-button>
</el-row>
</el-form-item>
<el-form-item label="随机种子">
<el-slider v-model="formData.prompt[14].inputs.seed" :min="1" :max="10000000" show-input />
</el-form-item>
<el-form-item label="cfg (数值越高,生图过程与提示词越相关)">
<el-slider v-model="formData.prompt[14].inputs.cfg" :max="20" show-input />
</el-form-item>
</el-form>
</el-col>
<el-col :span="16">
<div
:style="{ height: divHeight + 'px', 'overflow-y': 'auto', 'margin-bottom': '10px', 'background-color': '#f5f5f5', 'padding': '10px' }">
<div v-for="(resultItem, resultIndex) in resultList" :key="resultIndex">
<div style="display: flex; flex-wrap: wrap; justify-content: flex-end;">
<el-card style="max-width: 50%; margin-right: 10px; display: inline-block;background-color: lightblue;">
<div>
<p style="word-wrap: break-word; overflow-wrap: break-word;text-align: left">{{ resultItem }}</p>
</div>
</el-card>
</div>
<el-row :gutter="10" justify="center">
<el-col :span="6" v-for="(url, index) in pictureurl[resultIndex]" :key="index">
<el-card style="margin-bottom: 5%;height:95%">
<el-image style="width: 100%;" fit="cover" :src="url" :preview-src-list="pictureurl"
:initial-index="index" class="equal-size-image"></el-image>
<div style="text-align: center; margin-top: 15px">
<el-button :disabled="buttonStates[resultIndex][index].disabled" size="small" type="primary"
@click="saveImage(resultIndex, index, url, resultItem)">{{ buttonStates[resultIndex][index].text
}}</el-button>
</div>
</el-card>
</el-col>
</el-row>
</div>
<el-row :gutter="10" justify="center">
<el-col v-if="pictureLoading" :span="6" v-for="n in generateArray(this.skeletonNumber)" :key="n">
<el-card>
<el-skeleton class="custom-skeleton-item" animated>
<template #template>
<el-skeleton-item variant="image" class="custom-skeleton-item"></el-skeleton-item>
</template>
</el-skeleton>
</el-card>
</el-col>
</el-row>
<el-row :gutter="10" justify="center">
<el-col :span="12">
<el-card v-for="(item, index) in resultData" :key="index"
style="display: flex;flex-direction: column;align-items: center;margin-bottom: 10px" justify="center"
@click="handleCardClick(item)" :class="{ 'card-hover': !pictureLoading }">
<p style="word-wrap: break-word; overflow-wrap: break-word;">{{ item }}</p>
</el-card>
</el-col>
</el-row>
</div>
<div style="text-align: center">
<el-input style="width: 70%;" v-model="promptData" placeholder="试试输入你心中的画面,尽量描述具体点哦,可以按照这个格式来写: 提示词=主体+风格+场景"
type="textarea">
</el-input>
<el-button type="primary" :disabled="pictureLoading" @click="fetchData">
{{ !pictureLoading ? "生成图片" : "请等待" }}
</el-button>
</div>
</el-col>
</el-row>
</div>
</template>
<script>
import { convertTextToPicture, getQueue, getPromptId, getPicture, chattoprompt, textSensitiveWord, uploadPicture } from "@/api/aiGeneratedImage/index.js";
import CryptoJS from 'crypto-js'
import { useRoute } from 'vue-router'
export default {
data() {
return {
form: {
ratio: "512",
ratios2: "1/1",
},
ratioOptions: [
{ value: "512", label: "512" },
{ value: "768", label: "768" },
{ value: "1024", label: "1024" },
{ value: "1280", label: "1280" },
{ value: "2048", label: "2048" },
],
ratioOptions2: [
{ value: "1/1", label: "1:1" },
{ value: "4/3", label: "4:3" },
{ value: "3/4", label: "3:4" },
{ value: "9/16", label: "9:16" },
{ value: "16/9", label: "16:9" },
],
//
imageData: {
imageUrls: [],
prompt: [],
time: [],
id: [],
},
promptOption1: ["赛博朋克", "水墨风", "莫奈风格", "二次元", "中国风", "写实风格", "水彩风格", "工笔画", "素描风", "未来主义", "超现实主义", "映像派"],
promptOption2: ["男生", "女生", "老年人", "船舶", "蝴蝶", "狮子", "兔子", "飞机", "中年人", "大树", "长江", "坦克"],
promptOption3: ["雨林", "沙漠", "湖泊", "天空", "城市", "乡村", "太空", "教室"],
promptData: "",
//
formData: {
client_id: "533ef3a3-39c0-4e39-9ced-37d290f371f8",
prompt: {
3: {
inputs: {
images: ["10", 0],
},
class_type: "PreviewImage",
_meta: {
title: "Preview Image",
},
},
6: {
inputs: {
model: "Kwai-Kolors/Kolors",
precision: "fp16",
},
class_type: "DownloadAndLoadKolorsModel",
_meta: {
title: "(Down)load Kolors Model",
},
},
10: {
inputs: {
samples: ["14", 0],
vae: ["11", 0],
},
class_type: "VAEDecode",
_meta: {
title: "VAE Decode",
},
},
11: {
inputs: {
vae_name: "sdxl.vae.safetensors",
},
class_type: "VAELoader",
_meta: {
title: "Load VAE",
},
},
12: {
inputs: {
prompt: "",
negative_prompt: "",
num_images_per_prompt: 1,
chatglm3_model: ["13", 0],
},
class_type: "KolorsTextEncode",
_meta: {
title: "Kolors Text Encode",
},
},
13: {
inputs: {
precision: "quant8",
},
class_type: "DownloadAndLoadChatGLM3",
_meta: {
title: "(Down)load ChatGLM3 Model",
},
},
14: {
inputs: {
width: 1024,
height: 1024,
seed: 1000102404233412,
steps: 25,
cfg: 5,
scheduler: "EulerDiscreteScheduler",
denoise_strength: 1,
kolors_model: ["6", 0],
kolors_embeds: ["12", 0],
},
class_type: "KolorsSampler",
_meta: {
title: "Kolors Sampler",
},
},
},
},
picture: {
filename: "",
type: "temp",
subfolder: "",
preview: "",
channel: "",
},
buttonStates: [], //
resultList: [],
pictureurl: [],
percentage: 50,
num_picture: 1,
skeletonNumber: 0,
cfg_value: 5,
pictureLoading: false,
QueueNumber: 0, //
userIdInComponent: '',
activeName: '1',
checkList: [], //prompt
totalData: 0,
// deleteImageSrc: deletebutton,
timer: null,
isTimerPaused: false,
resultData: [],
divHeight: 0,
dataset_id: '',
courseName: '',
levelFirstId: '',
levelSecondId: '',
textbookId: '',
};
},
methods: {
//
generateArray(length) {
return Array.from({ length }, (_, index) => index);
},
//
Randomseed() {
this.formData.prompt[14].inputs.seed = Math.floor(
Math.random() * 10000001
);
},
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
},
//
async fetchData() {
// prompt
if (this.promptData == "") {
this.$message.error("prompt不能为空");
return;
}
const prompt = this.promptData
//
await textSensitiveWord(prompt)
this.setRatio(this.form.ratio)
this.changeSize()
this.resultList.push(this.promptData)
this.skeletonNumber = this.num_picture
const skeletonItemNumber = this.num_picture
// const userId = this.userIdInComponent
// const model = 'kolors'
// prompt
let temp = "";
if (skeletonItemNumber > 1) {
for (let i = 1; i < skeletonItemNumber; i++) {
temp += "|" + this.promptData;
}
this.formData.prompt[12].inputs.prompt = this.promptData + temp;
} else {
this.formData.prompt[12].inputs.prompt = this.promptData;
}
// json
const transformedData = {
client_id: this.formData.client_id,
prompt: Object.fromEntries(
Object.entries(this.formData.prompt).map(([key, value]) => [
key,
{
inputs: value.inputs,
class_type: value.class_type,
_meta: value._meta,
},
])
),
};
const jsonData = JSON.stringify(transformedData, null, 2);
//
try {
this.pictureLoading = true;
const response = await convertTextToPicture(jsonData); //
const pictureId = response.data.prompt_id;
const queue_Number = response.data.number
let queue;
do {
queue = await getQueue(); //
if (this.hasId(queue, pictureId)) {
this.QueueNumber = queue_Number - queue.data.queue_running[0][0] //
await this.sleep(500);
}
} while (this.hasId(queue, pictureId));
const pictureData = await getPromptId(pictureId); //id
const jsonString = JSON.parse(pictureData.request.responseText);
const urls = []
const buttonState = [];
for (let i = 0; i < skeletonItemNumber; i++) {
this.picture.filename = jsonString[pictureId].outputs[3].images[i].filename;
const pictureURL = await getPicture(this.picture); //url
const url0 = pictureURL.request.responseURL;
urls.push(url0)
buttonState.push({
disabled: false,
text: "插入本课素材资源库",
})
}
this.skeletonNumber = 0
this.pictureurl.push(urls)
this.buttonStates.push(buttonState)
this.pictureLoading = false;
} catch (error) {
this.pictureLoading = false;
console.error("Error fetching data:", error);
}
},
//
hasId(obj, id) {
if (typeof obj !== "object" || obj === null) {
return false;
}
if (Object.values(obj).includes(id)) {
return true;
}
return Object.values(obj).some((value) => this.hasId(value, id));
},
setRatio(ratio) {
this.form.ratio = ratio;
switch (ratio) {
case "512":
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height = 512;
break;
case "768":
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height = 768;
break;
case "1024":
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height = 1024;
break;
case "1280":
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height = 1280;
break;
case "2048":
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height = 2048;
break;
default:
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height = 512;
}
},
setRatio2(ratio) {
this.form.ratios2 = ratio;
},
changeSize() {
const a = this.form.ratios2
if (a == "1/1") {
this.formData.prompt[14].inputs.width = this.formData.prompt[14].inputs.height
} else if (a == "4/3") {
this.formData.prompt[14].inputs.height = (this.formData.prompt[14].inputs.width) * 3 / 4
} else if (a == "3/4") {
this.formData.prompt[14].inputs.width = (this.formData.prompt[14].inputs.height) * 3 / 4
} else if (a == "9/16") {
this.formData.prompt[14].inputs.width = (this.formData.prompt[14].inputs.height) * 9 / 16
} else {
this.formData.prompt[14].inputs.height = (this.formData.prompt[14].inputs.width) * 9 / 16
}
},
changePicture(num) {
this.num_picture = num;
},
//
async createPrompt() {
const dataset_id = this.dataset_id
const courseName = this.courseName
const prompt = `结合${courseName}给我三句详细的提示词用于生成图片以1.2.3的形式`
const response = await chattoprompt(dataset_id, prompt)
const resultData = response.data.answer
const promptData = []
for (let i = 1; i <= 3; i++) {
const startIndex = resultData.indexOf(`${i}.`) + `${i}.`.length;
const endIndex = resultData.indexOf("。", startIndex);
promptData.push(resultData.substring(startIndex, endIndex).trim());
}
this.resultData = promptData
},
handleCardClick(item) {
this.promptData = item
this.fetchData()
},
//
async saveImage(resultIndex, index, url, resultItem) {
this.buttonStates[resultIndex][index].disabled = true;
this.buttonStates[resultIndex][index].text = "正在保存...";
const numberIndex = url.indexOf('filename=');
const path = url.substring(numberIndex + 9);
const pngIndex = path.indexOf('.png');
const finalPath = path.substring(0, pngIndex + 4);
try {
const blob = await this.getImageBlob(`https://ai.ysaix.com:7853/view?filename=${finalPath}&type=temp`);
const hash = CryptoJS.MD5(blob).toString();
const formData = new FormData();
let file = new File([blob], `${resultItem}.png`, {
type: 'image/png'
})
//
formData.append('md5', hash);
formData.append('file', file);
formData.append('textbookId', this.textbookId);
formData.append('levelFirstId', this.levelFirstId);
formData.append('levelSecondId', this.levelSecondId);
formData.append('fileSource', '个人');
formData.append('fileRoot', '备课');
formData.append('fileShowName', `${resultItem}.png`);
formData.append('fileFlag', '素材');
const responsedata = uploadPicture(formData);
responsedata.then((response) => {
if (response.data && response.data.code === 200) {
//
this.buttonStates[resultIndex][index].text = "已保存";
this.buttonStates[resultIndex][index].disabled = true;
}
}).catch((error) => {
console.error(error);
this.buttonStates[resultIndex][index].disabled = false;
this.buttonStates[resultIndex][index].text = "插入本课素材资源库";
});
} catch (error) {
console.error(error);
}
},
getImageBlob(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error(`图片获取失败,状态码:${this.status}`));
}
};
xhr.onerror = function () {
reject(new Error('图片获取发生网络错误'));
};
xhr.send();
});
},
// convertImageToBase64(url) {
// return request({
// url: '/common/convertImageToBase64',
// method: 'get',
// params: {
// url: url
// }
// })
// },
// handleSaveImage(url) {
// this.convertImageToBase64(url).then(res => {
// this.$emit("saveImage", res)
// }, err => {
// this.$message.error(err);
// })
// },
//prompt
addCheckListToPrompt() {
const joinedString = this.checkList.join(',');
this.promptData = ''
this.promptData += joinedString;
},
//prompt
deletePrompt() {
this.promptData = '',
this.checkList = []
},
},
mounted() {
const route = useRoute();
this.dataset_id = route.query.datasetId;
this.courseName = route.query.coursetitle;
this.levelFirstId = route.query.levelFirstId;
this.levelSecondId = route.query.levelSecondId;
this.textbookId = route.query.textbookId;
this.createPrompt()
this.Randomseed();
this.divHeight = window.innerHeight * 0.80;
window.addEventListener('resize', () => {
this.divHeight = window.innerHeight * 0.85;
});
},
beforeDestroy() {
window.removeEventListener('resize', () => {
this.divHeight = window.innerHeight * 0.85;
});
}
};
</script>
<style scoped>
.el-row {
padding: 20px
}
.count-option,
.ratio-option {
width: 100%;
}
.custom-skeleton-item {
width: 100%;
height: 200px;
}
.time-display p {
font-size: 9px;
}
.equal-size-image img {
position: absolute;
top: 0;
}
.card-hover:hover {
background-color: #f0f0f0;
cursor: pointer;
}
.disabled-cursor {
cursor: not-allowed !important;
}
</style>

View File

@ -34,7 +34,8 @@ const getFileTypeIcon = () => {
gif: 'icon-gif',
txt: 'icon-txt',
rar: 'icon-rar',
apt: 'icon-A'
apt: 'icon-A',
aptist: 'icon-A',
}
if (iconObj[name]) {
return '#' + iconObj[name]

View File

@ -85,6 +85,12 @@ export const constantRoutes = [
name: 'questionUpload',
meta: { title: '习题上传' }
},
{
path: 'aiKolors',
component: () => import('@/components/ai-kolors/index.vue'),
name: 'aiKolors',
meta: { title: '文生图片' }
},
]
},

View File

@ -395,5 +395,6 @@ export const dataSetJson = {
"课标-高中-英语": "e889fcac9fd011efb22a0242ac140006",
"课标-高中-数学": "e03aa4fe9fd011ef91270242ac140006",
"课标-高中-地理": "270516829fd111efb13c0242ac140006",
"课标-高中-政治": "a7df2b01aafd11ef8bb40242ac140002",
"鉴权": "ragflow-IwMDI1MGU2YTU3NjExZWZiNWEzMDI0Mm"
}

View File

@ -13,6 +13,7 @@
<el-button type="success" @click="openPPTist">打开PPTist</el-button>
<el-button type="info" @click="onchange('/model/examination')">考试分析</el-button>
<el-button type="primary" v-menus="dt.menus">测试</el-button>
<el-button type="success" @click="onchange('/model/aiKolors')">文生图片</el-button>
</div>
</div>
</div>
@ -52,8 +53,11 @@ import { Plus, Refresh, Upload, Files, UploadFilled } from '@element-plus/icons-
import useUserStore from '@/store/modules/user' //
import msgUtils from '@/plugins/modal' //
import { createWindow, sessionStore } from '@/utils/tool' //
import * as API_smarttalk from '@/api/file' // api
import * as API_entpcourse from '@/api/education/entpcourse' // api
import * as API_entpcoursefile from '@/api/education/entpcoursefile' // api
import { dataSetJson } from '@/utils/comm' // id
import { sessionStore } from '@/utils/store' //
//
import ChooseTextbook from '@/components/choose-textbook/index.vue'
import { menusEvent } from '@/plugins/vue3-menus' //
@ -156,6 +160,21 @@ const onchange = (path) => {
if (path == '/model/newClassTaskAssign') {
//
router.push({ path, query: { courseObj: JSON.stringify(courseObj) } })
} else if (path == '/model/aiKolors') {
// ai
let subjectdata = sessionStore.get('subject.curNode')
let datasubject = `课标-${subjectdata.edustage}-${subjectdata.edusubject}`
console.log(subjectdata)
router.push({
path,
query: {
datasetId: dataSetJson[datasubject],
coursetitle: courseObj.coursetitle,
levelFirstId: subjectdata.parentid,
levelSecondId: subjectdata.id,
textbookId: subjectdata.rootid,
}
});
} else {
router.push(path)
}
@ -178,6 +197,19 @@ const getResourceList = async () => {
// HTTP
const HTTP_SERVER_API = (type, params = {}) => {
switch (type) {
case 'addSmarttalk': { //
const def = {
fileId: '', // id - Entpcoursefile id
fileFlag: 'aptist',
fileShowName: courseObj.coursetitle + '.aptist',
textbookId: courseObj.textbookId,
levelFirstId: courseObj.levelFirstId,
levelSecondId: courseObj.levelSecondId,
fileSource: '个人',
fileRoot: '备课'
}
return API_smarttalk.creatAPT({...def, ...params})
}
case 'addEntpcourse': { //
const node = courseObj.node || {}
if (!node) return msgUtils.msgWarning('请选择章节?')
@ -260,6 +292,8 @@ const handleAll = async(type, row) =>{
}
// ppt-(slide)
await HTTP_SERVER_API('addEntpcoursefile', params)
// -Smarttalk
await HTTP_SERVER_API('addSmarttalk',{fileId: id})
//
await getResourceList()
} else {

View File

@ -59,14 +59,16 @@
<!-- 手机登录 -->
<template #item_mobile>
<div>
<div>开始新的课堂需要点击先创建课堂才能显示手机二维码</div>
<div v-if="myClassActive.filetype=='apt'">开始新的课堂需要点击先创建课堂才能显示手机二维码</div>
<div v-else>开始新的课堂需要点击先创建课堂</div>
<el-button type="warning" :loading="dt.loading" @click="createClasscourse">创建课堂</el-button>
</div>
</template>
<!-- 故障备用 -->
<template #item_backup>
<div>
<div>如果手机扫码后进入课堂但本页面没自动跳转请点击下面按钮</div>
<div v-if="myClassActive.filetype=='apt'">如果手机扫码后进入课堂但本页面没自动跳转请点击下面按钮</div>
<div v-else>本页面没自动跳转请点击下面按钮</div>
<el-button type="primary" plain @click="classTeachingStart">开始上课</el-button>
</div>
</template>
@ -81,7 +83,7 @@
<script setup>
//
import { onMounted, reactive, ref, watchEffect, watch, nextTick } from 'vue' // vue
import { onMounted, reactive, ref, watchEffect, watch, nextTick, toRaw } from 'vue' // vue
import { Refresh } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus' // ui:
import vueQr from 'vue-qr/src/packages/vue-qr.vue' // :
@ -149,9 +151,9 @@ const open = async (id, classObj) => {
teacherForm.form.classcourseid = classObj.id
}
// im-chat
nextTick(async() => {
chat = await imChatRef.value?.initImChat()
})
// nextTick(async() => {
// chat = await imChatRef.value?.initImChat()
// })
}
}
//
@ -259,7 +261,15 @@ const createClasscourse = async () => {
dt.loading = false
// getClasscourseList('update') //
ElMessage.success('创建课程-成功')
// -pptList
if (myClassActive.value.filetype == 'aptist') {
const msgEl = ElMessage.warning({message:'正在打开公屏,请稍后...',duration: 0})
setTimeout(() => {
msgEl.close()
const classcourse = {...params, id: teacherForm.form.classcourseid}
openPublicScreen(classcourse)
}, 1500);
}
}
//
const removeClasscourse = async () => {
@ -323,6 +333,21 @@ const getQrUrl = async() => {
}
teacherForm.form.qrUrl = baseUrl + qrCodeUrl
}
//
const openPublicScreen = (classcourse) => {
const resource = toRaw(myClassActive.value)
sessionStore.set('curr.resource', resource) //
sessionStore.set('curr.classcourse', classcourse) //
createWindow('open-win', {
url: '/pptist', //
close: () => {
sessionStore.set('curr.resource', null) //
sessionStore.set('curr.classcourse', null) //
}
})
}
//
// ================== =======================

View File

@ -332,6 +332,10 @@ export default {
const { id, rootid } = sessionStore.get('subject.curNode')
const path="/teaching/aptindex?id="+items.fileId + "&unitId=" + id + "&bookId=" + rootid;
let configObj = outLink().getBaseData()
configObj.fullPath = 'https://localhost:7860/'
configObj.data.url = 'https://localhost:7860/'
configObj.data.domain = 'localhost'
console.log(configObj)
let fullPath = configObj.fullPath + path
fullPath = fullPath.replaceAll('//', '/')
//
@ -341,6 +345,8 @@ export default {
cookieData: { ...configObj.data }
})
return
} else if(items.fileFlag === 'aptist') { // aptist PPT-List
return this.$emit('change', 'click', items)
}
if (!items||!items.fileSuffix) return;
getPrepareById(items.id).then((item) => {

View File

@ -175,8 +175,9 @@ import { updateClasscourse } from '@/api/teaching/classcourse'
import { getClassInfo, getSelfReserv, endClass } from '@/api/classManage'
import { useGetHomework } from '@/hooks/useGetHomework'
import { editListItem } from '@/hooks/useClassTask'
import { addEntpcoursefileReturnId } from '@/api/education/entpcoursefile'
import { addEntpcoursefileReturnId, getEntpcoursefile } from '@/api/education/entpcoursefile'
import ClassReserv from '@/views/classManage/classReserv.vue'
import TreeLog from '@/views/prepare/components/treeLog.vue'
import classStart from './container/class-start.vue' //
import MsgEnum from '@/plugins/imChat/msgEnum' // im
import Chat from '@/utils/chat' // im
@ -239,7 +240,9 @@ export default {
activeClass: null,
pptDialog: false,
//
treelogRef:null
treelogRef:null,
// Entpcourse
entp: null
}
},
computed: {
@ -249,10 +252,12 @@ export default {
)
},
currentKJFileList() {
return this.currentFileList.filter((item) => item.fileFlag === 'apt' || item.fileFlag === '课件')
// return this.currentFileList.filter((item) => item.fileFlag === 'apt' || item.fileFlag === '')
return this.currentFileList.filter((item) => ['apt','aptist','课件'].includes(item.fileFlag))
},
currentSCFileList() {
return this.currentFileList.filter((item) => item.fileFlag !== 'apt' && item.fileFlag !== '课件')
// return this.currentFileList.filter((item) => item.fileFlag !== 'apt' && item.fileFlag !== '')
return this.currentFileList.filter((item) => !['apt','aptist','课件'].includes(item.fileFlag))
}
},
@ -305,8 +310,8 @@ export default {
//
startClass(item, classObj) {
// ()
const id = sessionStore.has('activeClass.id') ? sessionStore.get('activeClass.id') : null
if (id && id == item.id) return ElMessage.warning('当前正在上课,请勿重复操作')
// const id = sessionStore.has('activeClass.id') ? sessionStore.get('activeClass.id') : null
// if (id && id == item.id) return ElMessage.warning('')
// -store
sessionStore.set('activeClass', item)
this.activeClass = item
@ -316,6 +321,9 @@ export default {
if(item.fileFlag === 'apt') {
this.$refs.calssRef.open(item.fileId, classObj)
}
if(item.fileFlag === 'aptist') {
this.$refs.calssRef.open(item.fileId, classObj)
}
},
// -apt
async changeClass(type, row, other) {
@ -370,6 +378,28 @@ export default {
}, 1000)
break
}
case 'click': { // --aptist
if (row.fileFlag === 'aptist' && !!row.fileId) {
const res = await getEntpcoursefile(row.fileId)
if (res && res.code === 200) {
sessionStore.set('curr.resource', res.data) //
sessionStore.set('curr.smarttalk', row) // smarttalk
createWindow('open-win', {
url: '/pptist', //
close: () => {
sessionStore.set('curr.resource', null) //
sessionStore.set('curr.smarttalk', null) //
this.asyncAllFile() //
}
})
} else {
ElMessage.warning(res.msg||'文件获取异常!')
}
return
}
ElMessage.warning('该功能暂未开放!')
break
}
default:
break
}
@ -623,6 +653,7 @@ export default {
for (let i = 0; i < this.currentFileList.length; i++) {
let item = this.currentFileList[i]
if (item.fileFlag === 'apt') continue;
if (item.fileFlag === 'aptist') continue;
await asyncLocalFile(item)
}
this.asyncAllFileVisiable = false
@ -651,6 +682,11 @@ export default {
toolStore.curSubjectNode.querySearch = this.uploadData
this.initHomeWork()
await this.asyncAllFile()
//
const params = { evalid: this.currentNode.id, edituserid: this.userStore.userId, pageSize: 1 }
const res = await listEntpcourse(params)
this.entp = res?.rows?.[0] || null
sessionStore.set('curr.entp', this.entp) //
},
//
async initHomeWork() {