Merge branch 'master' of http://47.115.223.229:8888/yangjun/dbsd_kczx
This commit is contained in:
commit
a481d31fb2
|
@ -21,12 +21,15 @@ VITE_GLOB_API_URL=/jeecg-boot
|
|||
#后台接口全路径地址(必填)
|
||||
# VITE_GLOB_DOMAIN_URL=https://zxkccx.webvpn.nenu.edu.cn/jeecg-boot
|
||||
VITE_GLOB_DOMAIN_URL=https://smartedu.nenu.edu.cn/jeecg-boot
|
||||
# VITE_GLOB_DOMAIN_URL=https://xxhbtest2.nenu.edu.cn/jeecg-boot
|
||||
#VITE_GLOB_DOMAIN_URL=http://210.47.29.177
|
||||
# VITE_GLOB_DOMAIN_URL=https://kczxcs.nenu.edu.cn/jeecg-boot
|
||||
# VITE_GLOB_DOMAIN_URL=http://210.47.29.100/jeecg-boot
|
||||
|
||||
#RTC服务器地址
|
||||
# VITE_GLOB_RTC_SERVER = https://zxkccx.webvpn.nenu.edu.cn:8081
|
||||
VITE_GLOB_RTC_SERVER = https://smartedu.nenu.edu.cn:8081
|
||||
# VITE_GLOB_RTC_SERVER = https://xxhbtest2.nenu.edu.cn:8081
|
||||
|
||||
# 接口父路径前缀
|
||||
VITE_GLOB_API_URL_PREFIX=
|
||||
|
|
|
@ -161,7 +161,6 @@ watch(
|
|||
(param, oldParam) => {
|
||||
if (JSON.stringify(param) != JSON.stringify(oldParam)) {
|
||||
nextTick(() => {
|
||||
console.log(`🚀 ~ props.isGetBiz:`, props.isGetBiz, bindProps.value.disabled, loadBiz);
|
||||
if (props.isGetBiz && !bindProps.value.disabled) {
|
||||
//非被禁用状态,查询
|
||||
nextTick(() => {
|
||||
|
@ -644,8 +643,8 @@ function uploadFn(customRequestData) {
|
|||
|
||||
|
||||
console.log("🚀 ~ uploadFn ~ fileSize:", fileSize)
|
||||
if(fileSize>1024*1024*20){
|
||||
let jjj = {success:false,code:0,message:'文件大小不能超过200M,请重新上传',result:null,timestamp:new Date().getTime()}
|
||||
if(fileSize>1024*1024*150){
|
||||
let jjj = {success:false,code:0,message:'文件大小不能超过1500M,请重新上传',result:null,timestamp:new Date().getTime()}
|
||||
setTimeout(() => {
|
||||
onError(jjj, jjj, file);
|
||||
}, 1000);
|
||||
|
|
|
@ -29,20 +29,49 @@
|
|||
const chartRef = ref<HTMLDivElement | null>(null);
|
||||
const { setOptions, echarts } = useECharts(chartRef as Ref<HTMLDivElement>);
|
||||
const option = reactive({
|
||||
title: {
|
||||
text: '基础雷达图',
|
||||
},
|
||||
// title: {
|
||||
// text: '基础雷达图',
|
||||
// },
|
||||
legend: {
|
||||
data: ['文综', '理综'],
|
||||
// data: ['文综', '理综'],
|
||||
},
|
||||
// tooltip: {
|
||||
// trigger: 'item',
|
||||
// },
|
||||
radar: {
|
||||
name: {
|
||||
//指示器文字换行 start
|
||||
formatter: function(text){
|
||||
var strlength = text.length;
|
||||
if(strlength % 9 != 0){
|
||||
text = text.replace(/\S{9}/g,function(match){
|
||||
console.log(match);
|
||||
return match + '\n'
|
||||
})
|
||||
}else{
|
||||
text = text.replace(/\S{9}/g,function(match){
|
||||
console.log(match);
|
||||
return match + '\n'
|
||||
});
|
||||
strlength = text.length;
|
||||
text = text.substring(0,strlength - 1);
|
||||
}
|
||||
return text
|
||||
}
|
||||
//指示器文字换行 end
|
||||
},
|
||||
indicator: [
|
||||
{ name: '历史', max: 100 },
|
||||
{ name: '地理', max: 110 },
|
||||
{ name: '生物', max: 120 },
|
||||
{ name: '化学', max: 130 },
|
||||
{ name: '物理', max: 140 },
|
||||
{ name: '政治', max: 150 },
|
||||
{ value: 9.9, name: '重视学生的成长引领,关心学生的学习需求,乐于与学生交流', max: 10 },
|
||||
{ value: 9.9, name: '上课准备充分。课堂教学秩序良好。作业布置合理,反馈及时', max: 10 },
|
||||
{ value: 9.9, name: '教学目标明确,重点突出,逻辑性强', max: 10 },
|
||||
{ value: 9.9, name: '教学内容清晰易懂,体现适当的深度、广度和前沿性', max: 10 },
|
||||
{ value: 9.9, name: '教学方法合理。教学媒体得当,课程资源丰富', max: 10 },
|
||||
{ value: 9.9, name: '教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力', max: 10 },
|
||||
{ value: 9.9, name: '教学语言规范、准确,情绪饱满,热忱投入,为人师表', max: 10 },
|
||||
{ value: 9.9, name: '教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索', max: 10 },
|
||||
{ value: 9.9, name: '掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题', max: 10 },
|
||||
{ value: 9.9, name: '体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值', max: 10 },
|
||||
|
||||
],
|
||||
},
|
||||
series: [
|
||||
|
@ -50,7 +79,7 @@
|
|||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: [82, 70, 60, 55, 90, 66],
|
||||
value: [10,10,10,10,10,10,10,10,10,10,],
|
||||
name: '文综',
|
||||
},
|
||||
],
|
||||
|
@ -83,11 +112,13 @@
|
|||
let obj = { name: type };
|
||||
let chartArr = props.chartData.filter((item) => type === item.type);
|
||||
obj['value'] = chartArr.map((item) => item.value);
|
||||
obj['label'] = {show: true,}
|
||||
//data数据
|
||||
data.push(obj);
|
||||
});
|
||||
option.radar.indicator = indicator;
|
||||
option.series[0]['data'] = data;
|
||||
console.log("🚀 ~ initCharts ~ option:", option)
|
||||
setOptions(option);
|
||||
}
|
||||
return { chartRef };
|
||||
|
|
|
@ -385,6 +385,186 @@ export const columns: BasicColumn[] = [
|
|||
align: "center",
|
||||
dataIndex: 'col76'
|
||||
},
|
||||
{
|
||||
title: '1.坚持立德树人,秉持共育两代师表理念,积极落实有理想信念、 有道德情操、有扎实学识、有仁爱之心的“ 四有好老师 ”培养目标 要求。',
|
||||
align: "center",
|
||||
dataIndex: 'col78113'
|
||||
},
|
||||
{
|
||||
title: '2.坚持学术强师理念,全面掌握所教学科必需的专业知识与教学知 识,做学生锤炼品格、学习知识、创新思维、奉献祖国的引路人, 具备对所教课程进行高质量创新性教学设计、实施、评价、反思和 研究的能力。',
|
||||
align: "center",
|
||||
dataIndex: 'col79'
|
||||
},
|
||||
{
|
||||
title: '3.尊重学生的权利、地位和人格,注重培养学生爱国情怀、创新精 神和健康人格,促进学生全面发展、终身学习。',
|
||||
align: "center",
|
||||
dataIndex: 'col80'
|
||||
},
|
||||
{
|
||||
title: '4.把握学生身心发展规律和学习特点,了解学生学习基础,激发学 生学习动力,重视引导学生自主发展,为把学生培养成底蕴深厚、 专业扎实、教书育人能力过硬的卓越人才奠定坚实基础。',
|
||||
align: "center",
|
||||
dataIndex: 'col81'
|
||||
},
|
||||
{
|
||||
title: '5.熟悉所教学科的知识体系、历史脉络、思想方法、科际联系和发 展前沿,认识所教学科在课程体系中的地位及其与培养目标的关 系,并将课程理解有效转化为教学观念、内容和行为。',
|
||||
align: "center",
|
||||
dataIndex: 'col82'
|
||||
},
|
||||
{
|
||||
title: '6.熟悉所教课程的内容体系、教育价值、发展历程,有效融入中华 优秀文化、渗透课程思政,坚持教书和育人相统一、言传和身教 相统一、潜心问道和关注社会相统一、学术自由和学术规范相统一。',
|
||||
align: "center",
|
||||
dataIndex: 'col83'
|
||||
},
|
||||
{
|
||||
title: '7.认识课堂是教书育人、开展教学活动的专业场域,课堂活动对培 养学生理论素养、实践能力、创新精神具有独特价值,能够科学、 灵活、适切地选择和运用课堂组织形式。',
|
||||
align: "center",
|
||||
dataIndex: 'col84'
|
||||
},
|
||||
{
|
||||
title: '8.理解课堂教学是教与学活动的有机统一,了解课堂的构成要素、 组织类型、活动方式和基本特点,掌握所教课程的教学规律、教学 方法和评价手段。',
|
||||
align: "center",
|
||||
dataIndex: 'col85'
|
||||
},
|
||||
{
|
||||
title: '9.严慈相济,乐于与学生交流,关心学生身心健康,热心为学生成 长提供专业支持,促进学生自由而全面的发展。',
|
||||
align: "center",
|
||||
dataIndex: 'col86'
|
||||
},
|
||||
{
|
||||
title: '10.尊重学生个体差异,了解学生合理学习需求,关注学生思想情 感,善于发现学生的闪光点,对学生发展抱有积极期待。',
|
||||
align: "center",
|
||||
dataIndex: 'col87'
|
||||
},
|
||||
{
|
||||
title: '11.坚持正确的政治方向与价值导向,为党育人,为国育才,依法 执教,理解教师是学生学习的促进者和成长的引路人,自觉履行立 德树人使命。',
|
||||
align: "center",
|
||||
dataIndex: 'col88'
|
||||
},
|
||||
{
|
||||
title: '12.理解教学是教师的立身之本,教研是教师的强教之基,潜心教 学,发展教学学术,自觉遵守教学纪律,认真践行教学规范,保证 有质量的教学。',
|
||||
align: "center",
|
||||
dataIndex: 'col89'
|
||||
},
|
||||
{
|
||||
title: '13.重视师德师风修养,思想积极向上,热爱教育事业,为人师表, 具有正确的教育观、教师观、学生观。',
|
||||
align: "center",
|
||||
dataIndex: 'col90'
|
||||
},
|
||||
{
|
||||
title: '14.追求教学艺术境界,教学语言规范准确、富有逻辑性,教态自 然,举止文明,仪表得体,有亲和力,情绪饱满,热忱投入。',
|
||||
align: "center",
|
||||
dataIndex: 'col91'
|
||||
},
|
||||
{
|
||||
title: '15.立足创新人才培养目标,深入研究课程大纲,针对学生已有经 验和基础,确定教学目标、重难点,形成教学方案。',
|
||||
align: "center",
|
||||
dataIndex: 'col92'
|
||||
},
|
||||
{
|
||||
title: '16.合理规划教学环节和时间,恰当选择教学方法,积极推动人工 智能、智慧学习环境等新技术与学科课程融合,形成线上与线下教 学有机结合、深度融通的自主、合作、探究学习模式。',
|
||||
align: "center",
|
||||
dataIndex: 'col93'
|
||||
},
|
||||
{
|
||||
title: '17.开发和利用具有交互性、情境化的课程资源和教学案例,创设 良好的教学情境,科学选择学习评价方式,合理安排课后学习任务。',
|
||||
align: "center",
|
||||
dataIndex: 'col94'
|
||||
},
|
||||
{
|
||||
title: '18.清晰阐释课程内容,准确把握教学重难点,恰当解读学科本源 性思想和方法,适时融入课程领域发展新成果。',
|
||||
align: "center",
|
||||
dataIndex: 'col95'
|
||||
},
|
||||
{
|
||||
title: '19.创造性地生成教学过程,鼓励学生在实践探究中发现问题、提 出问题、解决问题,激发学生的好奇心、想象力与创新思维,培养 学生的创新能力。',
|
||||
align: "center",
|
||||
dataIndex: 'col96'
|
||||
},
|
||||
{
|
||||
title: '20.把握课堂教学节奏和进度,根据课堂情境和学生表现适时调整 教学方式,展现教学机智。',
|
||||
align: "center",
|
||||
dataIndex: 'col97'
|
||||
},
|
||||
{
|
||||
title: '21.落实教学评一体化,运用多种评价方式, 了解学生学习状态, 有效评估学生学习效果,及时给予反馈和有针对性的指导。',
|
||||
align: "center",
|
||||
dataIndex: 'col98'
|
||||
},
|
||||
{
|
||||
title: '22.掌握所学课程的基础知识、基本原理、基本思想等核心内容。',
|
||||
align: "center",
|
||||
dataIndex: 'col99'
|
||||
},
|
||||
{
|
||||
title: '23.理解课程的知识结构、思想体系,了解所学知识的发展脉络与 前沿动态,形成学科理解力。',
|
||||
align: "center",
|
||||
dataIndex: 'col100'
|
||||
},
|
||||
{
|
||||
title: '24.掌握所学知识的运用情境、策略和方法, 了解所学课程与其它 课程的关联,理解课程知识在当代社会发展中的意义和价值,形成 学科转化力。',
|
||||
align: "center",
|
||||
dataIndex: 'col101'
|
||||
},
|
||||
{
|
||||
title: '25.掌握所学课程的基本专业技能,运用所学知识与方法分析、解 决实际问题,在新情境中发展实践迁移力。',
|
||||
align: "center",
|
||||
dataIndex: 'col102'
|
||||
},
|
||||
{
|
||||
title: '26.具有大胆质疑、敢于创新的精神,对课程知识及其应用进行批 判性思考,形成反思研究力。',
|
||||
align: "center",
|
||||
dataIndex: 'col103'
|
||||
},
|
||||
{
|
||||
title: '27.能在观察和思考中发现学科与实践问题,基于证据和逻辑发表 自己的观点和见解,提出问题解决方案。',
|
||||
align: "center",
|
||||
dataIndex: 'col104'
|
||||
},
|
||||
{
|
||||
title: '28.具有理想信念、责任担当与家国情怀,形成服务国家、奉献社 会的专业品质。',
|
||||
align: "center",
|
||||
dataIndex: 'col105'
|
||||
},
|
||||
{
|
||||
title: '29.体会课程学习的挑战与乐趣,珍惜学习机会,规划学习过程, 养成自主学习习惯,达成课程学习的各项预设目标,发展学习力。',
|
||||
align: "center",
|
||||
dataIndex: 'col106'
|
||||
},
|
||||
{
|
||||
title: '30.凸显课程培根铸魂、启智润心的育人价值,培养德智体美劳全 面发展的高素质专业化创新型人才。改革教学方式方法,展现开拓 创新能力,体现教学创造性。',
|
||||
align: "center",
|
||||
dataIndex: 'col107'
|
||||
},
|
||||
{
|
||||
title: '31.准确把握学科本质属性,尊重学科学习规律,体现具有中国特 色、中国风格、中国气派的学科体系、学术体系和话语体系。',
|
||||
align: "center",
|
||||
dataIndex: 'col108'
|
||||
},
|
||||
{
|
||||
title: '32.吸纳学科教研新成果,融入学科相关的新理念、新知识、新技 术,关注学科交叉融合、协同创新的发展趋势。',
|
||||
align: "center",
|
||||
dataIndex: 'col109'
|
||||
},
|
||||
{
|
||||
title: '33.教学设计和教学实施渗透革新意识,积极探索新时代教育教学。',
|
||||
align: "center",
|
||||
dataIndex: 'col110'
|
||||
},
|
||||
{
|
||||
title: '34.教学理念、教学方法等呈现个性风貌,善于引导学生传承中华 师道,涵养教育情怀,成为学生为学、为事、为人的示范,体现教 书育人、言传身教相统一的教学艺术性。',
|
||||
align: "center",
|
||||
dataIndex: 'col111'
|
||||
},
|
||||
{
|
||||
title: '总分',
|
||||
align: "center",
|
||||
dataIndex: 'col112'
|
||||
},
|
||||
{
|
||||
title: '优点或建议',
|
||||
align: "center",
|
||||
dataIndex: 'col113'
|
||||
},
|
||||
];
|
||||
|
||||
//查询数据
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
<a-form-item label="评价类型">
|
||||
<j-dict-select-tag
|
||||
placeholder="请选择评价类型" v-model:value="queryParam.col15"
|
||||
:options="[{ value: '一般听课表', label: '一般听课表'}, { value: '线上听课表', label: '线上听课表'},{ value: '同行评价表', label: '同行评价表'},{ value: '老版评价表', label: '老版评价表'},{ value: '思政评价表', label: '思政评价表'}]"
|
||||
:options="[{ value: '一般听课表', label: '一般听课表'}, { value: '线上听课表', label: '线上听课表'},{ value: '同行评价表', label: '同行评价表'},{ value: '老版评价表', label: '老版评价表'},{ value: '思政评价表', label: '思政评价表'},{ value: '新版同行评价表', label: '新版同行评价表'}]"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
<template>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
|
||||
<a-row>
|
||||
<a-col :span="24" style="text-align: center">
|
||||
<div style="font-size: 20px;font-weight: bold;">{{formData.kcmc}}</div>
|
||||
<div style="line-height: 40px;">
|
||||
<span>{{formData.jsbh}} - </span>
|
||||
<span>{{formData.jsxm}} - </span>
|
||||
<span>{{formData.jsyx}} - </span>
|
||||
<span>{{formData.xqxn}}</span>
|
||||
</div>
|
||||
</a-col>
|
||||
<a-col :span="12" style="margin-top: 10px;">
|
||||
<a-card >
|
||||
<template #cover>
|
||||
<span style="font-size: 16px;font-weight: bold;margin: 30px 0 0 30px;">评价指标平均分</span>
|
||||
</template>
|
||||
<a-row>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />重视学生的成长引领,关心学生的学习需求,乐于与学生交流:{{formData.col1}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />上课准备充分。课堂教学秩序良好。作业布置合理,反馈及时:{{formData.col2}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学目标明确,重点突出,逻辑性强:{{formData.col3}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学内容清晰易懂,体现适当的深度、广度和前沿性:{{formData.col4}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学方法合理。教学媒体得当,课程资源丰富:{{formData.col5}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力:{{formData.col6}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学语言规范、准确,情绪饱满,热忱投入,为人师表:{{formData.col7}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索:{{formData.col8}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题:{{formData.col9}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值:{{formData.col10}}</span></a-col>
|
||||
<a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />优点或建议:{{formData.col11}}</span></a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col :span="12" style="margin-top: 10px;">
|
||||
<a-card >
|
||||
<template #cover>
|
||||
<span style="font-size: 16px;font-weight: bold;margin: 30px 0 0 30px;">雷达图</span>
|
||||
</template>
|
||||
<a-row>
|
||||
<a-col :span="24">
|
||||
<Radar :chartData="radarData" height="50vh"></Radar>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getValueType } from '/@/utils';
|
||||
import { saveOrUpdate } from '../KcXsktjxmydcp.api';
|
||||
import { Form } from 'ant-design-vue';
|
||||
import { Icon } from '/@/components/Icon';
|
||||
import Radar from '/@/components/chart/Radar.vue';
|
||||
|
||||
const radarData = ref<any>([]);
|
||||
const props = defineProps({
|
||||
formDisabled: { type: Boolean, default: false },
|
||||
formData: { type: Object, default: ()=>{} },
|
||||
formBpm: { type: Boolean, default: true }
|
||||
});
|
||||
const formRef = ref();
|
||||
const useForm = Form.useForm;
|
||||
const emit = defineEmits(['register', 'ok']);
|
||||
const formData = reactive<Record<string, any>>({
|
||||
id: '',
|
||||
kktzdh: '',
|
||||
kcbh: '',
|
||||
kcmc: '',
|
||||
jgh: '',
|
||||
skjs: '',
|
||||
kkdw: '',
|
||||
sxxs: '',
|
||||
sjxs: '',
|
||||
jzxs: '',
|
||||
jkxs: '',
|
||||
xsrs: '',
|
||||
ybzs: '',
|
||||
cpl: '',
|
||||
yxs: '',
|
||||
zhpj: '',
|
||||
col1: '',
|
||||
col2: '',
|
||||
col3: '',
|
||||
col4: '',
|
||||
col5: '',
|
||||
col6: '',
|
||||
col7: '',
|
||||
col8: '',
|
||||
col9: '',
|
||||
col10: '',
|
||||
xnxq: '',
|
||||
});
|
||||
const { createMessage } = useMessage();
|
||||
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||
const labelCol2 = ref<any>({ xs: { span: 24 }, sm: { span: 20 } });
|
||||
const wrapperCol2 = ref<any>({ xs: { span: 24 }, sm: { span: 4 } });
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
//表单验证
|
||||
const validatorRules = {
|
||||
};
|
||||
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: true });
|
||||
|
||||
// 表单禁用
|
||||
const disabled = computed(()=>{
|
||||
if(props.formBpm === true){
|
||||
if(props.formData.disabled === false){
|
||||
return false;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return props.formDisabled;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function add() {
|
||||
edit({});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function edit(record) {
|
||||
nextTick(() => {
|
||||
resetFields();
|
||||
//赋值
|
||||
Object.assign(formData, record);
|
||||
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />重视学生的成长引领,关心学生的学习需求,乐于与学生交流:{{formData.col1}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />上课准备充分。课堂教学秩序良好。作业布置合理,反馈及时:{{formData.col2}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学目标明确,重点突出,逻辑性强:{{formData.col3}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学内容清晰易懂,体现适当的深度、广度和前沿性:{{formData.col4}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学方法合理。教学媒体得当,课程资源丰富:{{formData.col5}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力:{{formData.col6}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学语言规范、准确,情绪饱满,热忱投入,为人师表:{{formData.col7}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索:{{formData.col8}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题:{{formData.col9}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值:{{formData.col10}}</span></a-col>
|
||||
// <a-col :span="24"><span class="pjzbClass"><Icon icon="ant-design:caret-right-outlined" />优点或建议:{{formData.col11}}</span></a-col>
|
||||
radarData.value = [
|
||||
{ value: formData.col1, name: '重视学生的成长引领,关心学生的学习需求,乐于与学生交流', max: 10 },
|
||||
{ value: formData.col2, name: '上课准备充分。课堂教学秩序良好。作业布置合理,反馈及时', max: 10 },
|
||||
{ value: formData.col3, name: '教学目标明确,重点突出,逻辑性强', max: 10 },
|
||||
{ value: formData.col4, name: '教学内容清晰易懂,体现适当的深度、广度和前沿性', max: 10 },
|
||||
{ value: formData.col5, name: '教学方法合理。教学媒体得当,课程资源丰富', max: 10 },
|
||||
{ value: formData.col6, name: '教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力', max: 10 },
|
||||
{ value: formData.col7, name: '教学语言规范、准确,情绪饱满,热忱投入,为人师表', max: 10 },
|
||||
{ value: formData.col8, name: '教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索', max: 10 },
|
||||
{ value: formData.col9, name: '掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题', max: 10 },
|
||||
{ value: formData.col10, name: '体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值', max: 10 },
|
||||
];
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交数据
|
||||
*/
|
||||
async function submitForm() {
|
||||
// 触发表单验证
|
||||
await validate();
|
||||
confirmLoading.value = true;
|
||||
const isUpdate = ref<boolean>(false);
|
||||
//时间格式化
|
||||
let model = formData;
|
||||
if (model.id) {
|
||||
isUpdate.value = true;
|
||||
}
|
||||
//循环数据
|
||||
for (let data in model) {
|
||||
//如果该数据是数组并且是字符串类型
|
||||
if (model[data] instanceof Array) {
|
||||
let valueType = getValueType(formRef.value.getProps, data);
|
||||
//如果是字符串类型的需要变成以逗号分割的字符串
|
||||
if (valueType === 'string') {
|
||||
model[data] = model[data].join(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
await saveOrUpdate(model, isUpdate.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
createMessage.success(res.message);
|
||||
emit('ok');
|
||||
} else {
|
||||
createMessage.warning(res.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
add,
|
||||
edit,
|
||||
submitForm,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.antd-modal-form {
|
||||
min-height: 500px !important;
|
||||
overflow-y: auto;
|
||||
padding: 24px 24px 24px 24px;
|
||||
}
|
||||
.xsxxClass{
|
||||
font-size: 16px;
|
||||
line-height: 40px;
|
||||
}
|
||||
.pjzbClass{
|
||||
font-size: 16px;
|
||||
line-height: 40px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
|
||||
<KcXsktjxmydcpDetailForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></KcXsktjxmydcpDetailForm>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, defineExpose } from 'vue';
|
||||
import KcXsktjxmydcpDetailForm from './KcXsktjxmydcpDetail2Form.vue'
|
||||
|
||||
const title = ref<string>('');
|
||||
const width = ref<string>('80%');
|
||||
const visible = ref<boolean>(false);
|
||||
const disableSubmit = ref<boolean>(false);
|
||||
const registerForm = ref();
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function add() {
|
||||
title.value = '新增';
|
||||
visible.value = true;
|
||||
nextTick(() => {
|
||||
registerForm.value.add();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param record
|
||||
*/
|
||||
function edit(record) {
|
||||
title.value = disableSubmit.value ? '详情' : '编辑';
|
||||
visible.value = true;
|
||||
nextTick(() => {
|
||||
registerForm.value.edit(record);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定按钮点击事件
|
||||
*/
|
||||
function handleOk() {
|
||||
registerForm.value.submitForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* form保存回调事件
|
||||
*/
|
||||
function submitCallback() {
|
||||
handleCancel();
|
||||
emit('success');
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消按钮回调事件
|
||||
*/
|
||||
function handleCancel() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
add,
|
||||
edit,
|
||||
disableSubmit,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/**隐藏样式-modal确定按钮 */
|
||||
.jee-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
|
||||
<div style="width:100%;text-align: right;line-height:40px;">
|
||||
<a style="padding-right: 0.5rem;" @click="init()">刷新</a>
|
||||
<!-- <RouterLink to="/site/xspjjgore">查看更多</RouterLink> -->
|
||||
</div>
|
||||
<a-list item-layout="horizontal" :data-source="list" :grid="{ gutter: 16, xs: 2, sm: 4, md: 4, lg: 4, xl: 4, xxl: 4, xxxl: 4 }">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item>
|
||||
<div style="border: 2px #eef1f2 solid;">
|
||||
<div>
|
||||
<div style="width: 100%;height: 20px;background-color: #1c84c6;"></div>
|
||||
<div style="width:100%;white-space:normal; word-break:break-all;overflow:hidden;padding: 10px;height: 70px;font-weight: 600;font-size: 16px;">
|
||||
{{ item.kcmc }}
|
||||
</div>
|
||||
</div>
|
||||
<a-divider style="margin: 0px;color: #eef1f2;" />
|
||||
<div style="padding: 20px;font-weight: 600;">
|
||||
<a-row>
|
||||
<a-col :span="15">
|
||||
<div style="height: 38px;font-size: 16px;font-weight: 700;">{{ item.skjs }}</div>
|
||||
<div style="font-size: 14px;font-weight: 700;height: 50px;">{{ item.kkyx }}</div>
|
||||
</a-col>
|
||||
<a-col :span="9" style="text-align: center;height: 70px;background-color: #e2e2e2;border-radius: 5px;">
|
||||
<div style="color: #1c84c6;font-size: 20px;font-weight: 600;margin-top: 10px;">{{ item.cpl }}</div>
|
||||
<div style="font-size: 14px;font-weight: 700;">参评率</div>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24" style="margin-top:0px;font-weight: 700;">
|
||||
<div>平均分汇总:{{ item.pjfhz }}</div>
|
||||
<div>参 评 率 :{{ item.cpl }}</div>
|
||||
<!-- <div>有 效 数 :{{ item.yxs }}</div> -->
|
||||
<div>学期学年 :{{ item.xqxn }}</div>
|
||||
</a-col>
|
||||
|
||||
<a-col :span="24">
|
||||
<a-row style="text-align: center;">
|
||||
<a-col :span="24">
|
||||
<a-button type="primary" class="bcClass" @click="openJspjPage(item)" >详情</a-button>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
<a-pagination v-model="current" :total="total" show-less-items @change="handlePageChange" v-if="props.flagPage" style="text-align: right;" :hideOnSinglePage="true"/>
|
||||
|
||||
<KcXsktjxmydcpDetailModal ref="KcXsktjxmydcpDetailModalPage"/>
|
||||
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { TeamOutlined, FormOutlined } from '@ant-design/icons-vue';
|
||||
import { Pagination } from 'ant-design-vue';
|
||||
import { getUserId, getSysConfig } from '/@/views/site/utils/index';
|
||||
|
||||
import KcXsktjxmydcpDetailModal from '/@/views/kc/kcXsktjxmydcp/components/KcXsktjxmydcpDetail2Modal.vue';
|
||||
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
enum Api {
|
||||
list = '/pjxxJszbpjf/pjxxJszbpjf/listIndex'
|
||||
}
|
||||
|
||||
const KcXsktjxmydcpDetailModalPage = ref();
|
||||
|
||||
const current = ref<number>(0);
|
||||
const total = ref<number>(2);
|
||||
const loadingList = ref<boolean>(false);
|
||||
|
||||
const APagination = Pagination;
|
||||
const emit = defineEmits(['changeParam']);
|
||||
|
||||
const props = defineProps({
|
||||
queryParam: { type: Object, default: () => ({}) },
|
||||
flagPage: { type:Boolean,default:false}
|
||||
});
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
const listApi = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
const list = ref<any>([]);
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.queryParam,
|
||||
(v) => init(),
|
||||
{
|
||||
deep:true,
|
||||
immediate:true,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
function init() {
|
||||
console.log('init');
|
||||
loadingList.value = true
|
||||
listApi({ jsbh: getUserId(), ...props.queryParam }).then(res => {
|
||||
list.value = res?.records ?? [];
|
||||
|
||||
total.value = res.total;
|
||||
current.value = res.current;
|
||||
list.value = res.records
|
||||
loadingList.value = false
|
||||
});
|
||||
}
|
||||
|
||||
function handlePageChange(record){
|
||||
emit('changeParam',record);
|
||||
// loadData();
|
||||
}
|
||||
/**
|
||||
* 转换时间由【0012】转成【00:12】
|
||||
* @param time 四个字符,时分,无分隔
|
||||
*/
|
||||
function formatTime(time: string) {
|
||||
if(!time) return '';
|
||||
let t_i_m_e = time.split('');
|
||||
return [t_i_m_e[0],t_i_m_e[1],':',t_i_m_e[2],t_i_m_e[3]].join('');
|
||||
}
|
||||
function onSearch() {
|
||||
init();
|
||||
}
|
||||
|
||||
//教师评价
|
||||
function openJspjPage(item) {
|
||||
KcXsktjxmydcpDetailModalPage.value.disableSubmit = true;
|
||||
KcXsktjxmydcpDetailModalPage.value.edit(item)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
onSearch
|
||||
});
|
||||
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.wenZiJuZhong {
|
||||
text-align: center;
|
||||
}
|
||||
.wenZiJiaCu {
|
||||
font-weight: 700;
|
||||
}
|
||||
.fs1d1r {
|
||||
font-size: 16px;
|
||||
min-height: 55.281px;
|
||||
}
|
||||
.hand {
|
||||
cursor:pointer;
|
||||
}
|
||||
.dateAndTime {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.yyyClass{
|
||||
background: #6cafda;font-weight: 600;color:#fff;border-radius: 5px;line-height: 23px;
|
||||
}
|
||||
.yyClass{
|
||||
background-color: #1c84c6;font-weight: 600;color:#fff;border-radius: 5px;line-height: 23px;
|
||||
}
|
||||
.bcClass{
|
||||
background-color: #1c84c6;font-weight: 600;border-radius: 5px;line-height: 23px;
|
||||
}
|
||||
|
||||
.itemDate {
|
||||
border-radius: 25px;
|
||||
background: #cccccc8c;
|
||||
width: 90%;
|
||||
margin-bottom: 0.5rem;
|
||||
text-align: center;
|
||||
margin: 0 auto .5rem;
|
||||
padding: 0.5rem;
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,72 @@
|
|||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from "/@/hooks/web/useMessage";
|
||||
|
||||
const { createConfirm } = useMessage();
|
||||
|
||||
enum Api {
|
||||
list = '/pjxxJszbpjf/pjxxJszbpjf/list',
|
||||
save='/pjxxJszbpjf/pjxxJszbpjf/add',
|
||||
edit='/pjxxJszbpjf/pjxxJszbpjf/edit',
|
||||
deleteOne = '/pjxxJszbpjf/pjxxJszbpjf/delete',
|
||||
deleteBatch = '/pjxxJszbpjf/pjxxJszbpjf/deleteBatch',
|
||||
importExcel = '/pjxxJszbpjf/pjxxJszbpjf/importExcel',
|
||||
exportXls = '/pjxxJszbpjf/pjxxJszbpjf/exportXls',
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出api
|
||||
* @param params
|
||||
*/
|
||||
export const getExportUrl = Api.exportXls;
|
||||
|
||||
/**
|
||||
* 导入api
|
||||
*/
|
||||
export const getImportUrl = Api.importExcel;
|
||||
|
||||
/**
|
||||
* 列表接口
|
||||
* @param params
|
||||
*/
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
/**
|
||||
* 删除单个
|
||||
* @param params
|
||||
* @param handleSuccess
|
||||
*/
|
||||
export const deleteOne = (params,handleSuccess) => {
|
||||
return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
* @param params
|
||||
* @param handleSuccess
|
||||
*/
|
||||
export const batchDelete = (params, handleSuccess) => {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: '确认删除',
|
||||
content: '是否删除选中数据',
|
||||
okText: '确认',
|
||||
cancelText: '取消',
|
||||
onOk: () => {
|
||||
return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => {
|
||||
handleSuccess();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存或者更新
|
||||
* @param params
|
||||
* @param isUpdate
|
||||
*/
|
||||
export const saveOrUpdate = (params, isUpdate) => {
|
||||
let url = isUpdate ? Api.edit : Api.save;
|
||||
return defHttp.post({ url: url, params }, { isTransformResponse: false });
|
||||
}
|
|
@ -0,0 +1,232 @@
|
|||
import {BasicColumn} from '/@/components/Table';
|
||||
import {FormSchema} from '/@/components/Table';
|
||||
import { rules} from '/@/utils/helper/validator';
|
||||
import { render } from '/@/utils/common/renderUtils';
|
||||
//列表数据
|
||||
export const columns: BasicColumn[] = [
|
||||
{
|
||||
title: '教师编号',
|
||||
align: "center",
|
||||
dataIndex: 'jsbh'
|
||||
},
|
||||
{
|
||||
title: '教师姓名',
|
||||
align: "center",
|
||||
dataIndex: 'jsxm'
|
||||
},
|
||||
{
|
||||
title: '教师职称',
|
||||
align: "center",
|
||||
dataIndex: 'jszc'
|
||||
},
|
||||
{
|
||||
title: '教师院系',
|
||||
align: "center",
|
||||
dataIndex: 'jsyx'
|
||||
},
|
||||
{
|
||||
title: '开课院系',
|
||||
align: "center",
|
||||
dataIndex: 'kkyx'
|
||||
},
|
||||
{
|
||||
title: '课程名称',
|
||||
align: "center",
|
||||
dataIndex: 'kcmc'
|
||||
},
|
||||
{
|
||||
title: '开课编号',
|
||||
align: "center",
|
||||
dataIndex: 'kkbh'
|
||||
},
|
||||
{
|
||||
title: '参评率',
|
||||
align: "center",
|
||||
dataIndex: 'cpl'
|
||||
},
|
||||
{
|
||||
title: '平均分汇总',
|
||||
align: "center",
|
||||
dataIndex: 'pjfhz'
|
||||
},
|
||||
{
|
||||
title: '重视学生的成长引领,关心学生的学习需求,乐于与学生交流',
|
||||
align: "center",
|
||||
dataIndex: 'col1'
|
||||
},
|
||||
{
|
||||
title: '上课准备充分,课堂教学秩序良好。作业布置合理,反馈及时',
|
||||
align: "center",
|
||||
dataIndex: 'col2'
|
||||
},
|
||||
{
|
||||
title: '教学目标明确,重点突出,逻辑性强',
|
||||
align: "center",
|
||||
dataIndex: 'col3'
|
||||
},
|
||||
{
|
||||
title: '教学内容清晰易懂,体现适当的深度、广度和前沿性',
|
||||
align: "center",
|
||||
dataIndex: 'col4'
|
||||
},
|
||||
{
|
||||
title: '教学方法合理,教学媒体得当,课程资源丰富',
|
||||
align: "center",
|
||||
dataIndex: 'col5'
|
||||
},
|
||||
{
|
||||
title: '教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力',
|
||||
align: "center",
|
||||
dataIndex: 'col6'
|
||||
},
|
||||
{
|
||||
title: '教学语言规范、准确,情绪饱满,热忱投入,为人师表',
|
||||
align: "center",
|
||||
dataIndex: 'col7'
|
||||
},
|
||||
{
|
||||
title: '教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索',
|
||||
align: "center",
|
||||
dataIndex: 'col8'
|
||||
},
|
||||
{
|
||||
title: '掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题',
|
||||
align: "center",
|
||||
dataIndex: 'col9'
|
||||
},
|
||||
{
|
||||
title: '体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值',
|
||||
align: "center",
|
||||
dataIndex: 'col10'
|
||||
},
|
||||
{
|
||||
title: '优点或建议',
|
||||
align: "center",
|
||||
dataIndex: 'col11'
|
||||
},
|
||||
{
|
||||
title: '学期学年',
|
||||
align: "center",
|
||||
dataIndex: 'xqxn'
|
||||
},
|
||||
];
|
||||
|
||||
//查询数据
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
];
|
||||
|
||||
//表单数据
|
||||
export const formSchema: FormSchema[] = [
|
||||
{
|
||||
label: '教师编号',
|
||||
field: 'jsbh',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教师姓名',
|
||||
field: 'jsxm',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教师职称',
|
||||
field: 'jszc',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教师院系',
|
||||
field: 'jsyx',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '开课院系',
|
||||
field: 'kkyx',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '课程名称',
|
||||
field: 'kcmc',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '开课编号',
|
||||
field: 'kkbh',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '参评率',
|
||||
field: 'cpl',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '平均分汇总',
|
||||
field: 'pjfhz',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '重视学生的成长引领,关心学生的学习需求,乐于与学生交流',
|
||||
field: 'col1',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '上课准备充分,课堂教学秩序良好。作业布置合理,反馈及时',
|
||||
field: 'col2',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教学目标明确,重点突出,逻辑性强',
|
||||
field: 'col3',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教学内容清晰易懂,体现适当的深度、广度和前沿性',
|
||||
field: 'col4',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教学方法合理,教学媒体得当,课程资源丰富',
|
||||
field: 'col5',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力',
|
||||
field: 'col6',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教学语言规范、准确,情绪饱满,热忱投入,为人师表',
|
||||
field: 'col7',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索',
|
||||
field: 'col8',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题',
|
||||
field: 'col9',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值',
|
||||
field: 'col10',
|
||||
component: 'Input',
|
||||
},
|
||||
{
|
||||
label: '优点或建议',
|
||||
field: 'col11',
|
||||
component: 'InputTextArea',
|
||||
},
|
||||
{
|
||||
label: '学期学年',
|
||||
field: 'xqxn',
|
||||
component: 'Input',
|
||||
},
|
||||
// TODO 主键隐藏字段,目前写死为ID
|
||||
{
|
||||
label: '',
|
||||
field: 'id',
|
||||
component: 'Input',
|
||||
show: false,
|
||||
},
|
||||
];
|
|
@ -0,0 +1,255 @@
|
|||
<template>
|
||||
<div>
|
||||
<!--查询区域-->
|
||||
<div class="jeecg-basic-table-form-container">
|
||||
<a-form @keyup.enter.native="searchQuery" :model="queryParam" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-row :gutter="24">
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="教师姓名">
|
||||
<j-input placeholder="请填写教师姓名" v-model:value="queryParam.jsxm" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="课程名称">
|
||||
<j-input placeholder="请填写课程名称" v-model:value="queryParam.kcmc" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="开课单位">
|
||||
<JDictSelectTag placeholder="请选择开课单位" v-model:value="queryParam.jsyx" :dictCode="`kc_kkdw_view,kkdw,kkdw`"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8">
|
||||
<a-form-item label="开课单位">
|
||||
<JDictSelectTag placeholder="请选择开课单位" v-model:value="queryParam.xqxn" :dictCode="`kc_xqxn_history,title,title`"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :lg="8">
|
||||
<span style="float: left; overflow: hidden" class="table-page-search-submitButtons">
|
||||
<a-col :lg="6">
|
||||
<a-button type="primary" preIcon="ant-design:search-outlined" @click="searchQuery">查询</a-button>
|
||||
</a-col>
|
||||
</span>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</div>
|
||||
<!--引用表格-->
|
||||
<BasicTable @register="registerTable" >
|
||||
<!--插槽:table标题-->
|
||||
<template #tableTitle>
|
||||
<!-- <a-button type="primary" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button> -->
|
||||
<a-button type="primary" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||
<j-upload-button type="primary" preIcon="ant-design:import-outlined" @click="onImportXls">导入</j-upload-button>
|
||||
<!-- <a-dropdown v-if="selectedRowKeys.length > 0">
|
||||
<template #overlay>
|
||||
<a-menu>
|
||||
<a-menu-item key="1" @click="batchHandleDelete">
|
||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||
删除
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</template>
|
||||
<a-button>批量操作
|
||||
<Icon icon="mdi:chevron-down"></Icon>
|
||||
</a-button>
|
||||
</a-dropdown> -->
|
||||
</template>
|
||||
<!--操作栏-->
|
||||
<template #action="{ record }">
|
||||
<TableAction :actions="getTableAction(record)"/>
|
||||
</template>
|
||||
<!--字段回显插槽-->
|
||||
<template #htmlSlot="{text}">
|
||||
<div v-html="text"></div>
|
||||
</template>
|
||||
<!--省市区字段回显插槽-->
|
||||
<!--<template #pcaSlot="{text}">
|
||||
{{ getAreaTextByCode(text) }}
|
||||
</template>-->
|
||||
<template #fileSlot="{text}">
|
||||
<span v-if="!text" style="font-size: 12px;font-style: italic;">无文件</span>
|
||||
<a-button v-else :ghost="true" type="primary" preIcon="ant-design:download-outlined" size="small" @click="downloadFile(text)">下载</a-button>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<!-- 表单区域 -->
|
||||
<PjxxJszbpjfModal ref="registerModal" @success="handleSuccess"></PjxxJszbpjfModal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="pjxxJszbpjf-pjxxJszbpjf" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { BasicTable, useTable, TableAction } from '/@/components/Table';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import { columns } from './PjxxJszbpjf.data';
|
||||
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './PjxxJszbpjf.api';
|
||||
import { downloadFile } from '/@/utils/common/renderUtils';
|
||||
import PjxxJszbpjfModal from './components/PjxxJszbpjfModal.vue'
|
||||
import { JInput } from '/@/components/Form';
|
||||
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||
|
||||
const queryParam = ref<any>({});
|
||||
const toggleSearchStatus = ref<boolean>(false);
|
||||
const registerModal = ref();
|
||||
//注册table数据
|
||||
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
||||
tableProps: {
|
||||
title: '学生评教结果',
|
||||
api: list,
|
||||
columns,
|
||||
canResize:false,
|
||||
useSearchForm: false,
|
||||
actionColumn: {
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
},
|
||||
beforeFetch: (params) => {
|
||||
params.column = '',params.order = '';//新生成的默认不带排序
|
||||
return Object.assign(params, queryParam.value);
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: "学生评教结果",
|
||||
url: getExportUrl,
|
||||
params: () => {
|
||||
let params:any = {};
|
||||
return Object.assign(params,queryParam.value);
|
||||
}
|
||||
},
|
||||
importConfig: {
|
||||
url: getImportUrl,
|
||||
success: handleSuccess
|
||||
},
|
||||
});
|
||||
const [registerTable, { reload, collapseAll, updateTableDataRecord, findTableDataRecord, getDataSource }, { rowSelection, selectedRowKeys }] = tableContext;
|
||||
const labelCol = reactive({
|
||||
xs: { span: 24 },
|
||||
sm: { span: 7 },
|
||||
});
|
||||
const wrapperCol = reactive({
|
||||
xs: { span: 24 },
|
||||
sm: { span: 16 },
|
||||
});
|
||||
|
||||
/**
|
||||
* 新增事件
|
||||
*/
|
||||
function handleAdd() {
|
||||
registerModal.value.disableSubmit = false;
|
||||
registerModal.value.add();
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑事件
|
||||
*/
|
||||
function handleEdit(record: Recordable) {
|
||||
registerModal.value.disableSubmit = false;
|
||||
registerModal.value.edit(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
function handleDetail(record: Recordable) {
|
||||
registerModal.value.disableSubmit = true;
|
||||
registerModal.value.edit(record);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除事件
|
||||
*/
|
||||
async function handleDelete(record) {
|
||||
await deleteOne({ id: record.id }, handleSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除事件
|
||||
*/
|
||||
async function batchHandleDelete() {
|
||||
await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* 成功回调
|
||||
*/
|
||||
function handleSuccess() {
|
||||
(selectedRowKeys.value = []) && reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作栏
|
||||
*/
|
||||
function getTableAction(record) {
|
||||
return [
|
||||
// {
|
||||
// label: '编辑',
|
||||
// onClick: handleEdit.bind(null, record),
|
||||
// },
|
||||
{
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 下拉操作栏
|
||||
*/
|
||||
function getDropDownAction(record) {
|
||||
return [
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
}, {
|
||||
label: '删除',
|
||||
popConfirm: {
|
||||
title: '是否确认删除',
|
||||
confirm: handleDelete.bind(null, record),
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询
|
||||
*/
|
||||
function searchQuery() {
|
||||
reload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置
|
||||
*/
|
||||
function searchReset() {
|
||||
queryParam.value = {};
|
||||
selectedRowKeys.value = [];
|
||||
//刷新数据
|
||||
reload();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.jeecg-basic-table-form-container {
|
||||
.table-page-search-submitButtons {
|
||||
display: block;
|
||||
margin-bottom: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.query-group-cust{
|
||||
width: calc(50% - 15px);
|
||||
min-width: 100px !important;
|
||||
}
|
||||
.query-group-split-cust{
|
||||
width: 30px;
|
||||
display: inline-block;
|
||||
text-align: center
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,247 @@
|
|||
<template>
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol">
|
||||
<a-row>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教师编号" v-bind="validateInfos.jsbh">
|
||||
<a-input v-model:value="formData.jsbh" placeholder="请输入教师编号" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教师姓名" v-bind="validateInfos.jsxm">
|
||||
<a-input v-model:value="formData.jsxm" placeholder="请输入教师姓名" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教师职称" v-bind="validateInfos.jszc">
|
||||
<a-input v-model:value="formData.jszc" placeholder="请输入教师职称" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教师院系" v-bind="validateInfos.jsyx">
|
||||
<a-input v-model:value="formData.jsyx" placeholder="请输入教师院系" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="开课院系" v-bind="validateInfos.kkyx">
|
||||
<a-input v-model:value="formData.kkyx" placeholder="请输入开课院系" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="课程名称" v-bind="validateInfos.kcmc">
|
||||
<a-input v-model:value="formData.kcmc" placeholder="请输入课程名称" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="开课编号" v-bind="validateInfos.kkbh">
|
||||
<a-input v-model:value="formData.kkbh" placeholder="请输入开课编号" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="参评率" v-bind="validateInfos.cpl">
|
||||
<a-input v-model:value="formData.cpl" placeholder="请输入参评率" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="平均分汇总" v-bind="validateInfos.pjfhz">
|
||||
<a-input v-model:value="formData.pjfhz" placeholder="请输入平均分汇总" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="重视学生的成长引领,关心学生的学习需求,乐于与学生交流" v-bind="validateInfos.col1">
|
||||
<a-input v-model:value="formData.col1" placeholder="请输入重视学生的成长引领,关心学生的学习需求,乐于与学生交流" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="上课准备充分,课堂教学秩序良好。作业布置合理,反馈及时" v-bind="validateInfos.col2">
|
||||
<a-input v-model:value="formData.col2" placeholder="请输入上课准备充分,课堂教学秩序良好。作业布置合理,反馈及时" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教学目标明确,重点突出,逻辑性强" v-bind="validateInfos.col3">
|
||||
<a-input v-model:value="formData.col3" placeholder="请输入教学目标明确,重点突出,逻辑性强" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教学内容清晰易懂,体现适当的深度、广度和前沿性" v-bind="validateInfos.col4">
|
||||
<a-input v-model:value="formData.col4" placeholder="请输入教学内容清晰易懂,体现适当的深度、广度和前沿性" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教学方法合理,教学媒体得当,课程资源丰富" v-bind="validateInfos.col5">
|
||||
<a-input v-model:value="formData.col5" placeholder="请输入教学方法合理,教学媒体得当,课程资源丰富" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力" v-bind="validateInfos.col6">
|
||||
<a-input v-model:value="formData.col6" placeholder="请输入教师知识渊博,教学水平高,教学特色鲜明,教学有吸引力" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教学语言规范、准确,情绪饱满,热忱投入,为人师表" v-bind="validateInfos.col7">
|
||||
<a-input v-model:value="formData.col7" placeholder="请输入教学语言规范、准确,情绪饱满,热忱投入,为人师表" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索" v-bind="validateInfos.col8">
|
||||
<a-input v-model:value="formData.col8" placeholder="请输入教学中启发学生思考,引导自主学习,激发学习动力,鼓励创新探索" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题" v-bind="validateInfos.col9">
|
||||
<a-input v-model:value="formData.col9" placeholder="请输入掌握了所学课程的基础知识和基本理论,能用所学知识解决实际问题" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值" v-bind="validateInfos.col10">
|
||||
<a-input v-model:value="formData.col10" placeholder="请输入体会到本门课程学习的乐趣和挑战,理解课程内容的意义与价值" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="优点或建议" v-bind="validateInfos.col11">
|
||||
<a-textarea v-model:value="formData.col11" rows="4" placeholder="请输入优点或建议" :disabled="disabled"/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="24">
|
||||
<a-form-item label="学期学年" v-bind="validateInfos.xqxn">
|
||||
<a-input v-model:value="formData.xqxn" placeholder="请输入学期学年" :disabled="disabled"></a-input>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import { getValueType } from '/@/utils';
|
||||
import { saveOrUpdate } from '../PjxxJszbpjf.api';
|
||||
import { Form } from 'ant-design-vue';
|
||||
|
||||
const props = defineProps({
|
||||
formDisabled: { type: Boolean, default: false },
|
||||
formData: { type: Object, default: ()=>{} },
|
||||
formBpm: { type: Boolean, default: true }
|
||||
});
|
||||
const formRef = ref();
|
||||
const useForm = Form.useForm;
|
||||
const emit = defineEmits(['register', 'ok']);
|
||||
const formData = reactive<Record<string, any>>({
|
||||
id: '',
|
||||
jsbh: '',
|
||||
jsxm: '',
|
||||
jszc: '',
|
||||
jsyx: '',
|
||||
kkyx: '',
|
||||
kcmc: '',
|
||||
kkbh: '',
|
||||
cpl: '',
|
||||
pjfhz: '',
|
||||
col1: '',
|
||||
col2: '',
|
||||
col3: '',
|
||||
col4: '',
|
||||
col5: '',
|
||||
col6: '',
|
||||
col7: '',
|
||||
col8: '',
|
||||
col9: '',
|
||||
col10: '',
|
||||
col11: '',
|
||||
xqxn: '',
|
||||
});
|
||||
const { createMessage } = useMessage();
|
||||
const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 5 } });
|
||||
const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 16 } });
|
||||
const confirmLoading = ref<boolean>(false);
|
||||
//表单验证
|
||||
const validatorRules = {
|
||||
};
|
||||
const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: true });
|
||||
|
||||
// 表单禁用
|
||||
const disabled = computed(()=>{
|
||||
if(props.formBpm === true){
|
||||
if(props.formData.disabled === false){
|
||||
return false;
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return props.formDisabled;
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function add() {
|
||||
edit({});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
*/
|
||||
function edit(record) {
|
||||
nextTick(() => {
|
||||
resetFields();
|
||||
//赋值
|
||||
Object.assign(formData, record);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 提交数据
|
||||
*/
|
||||
async function submitForm() {
|
||||
// 触发表单验证
|
||||
await validate();
|
||||
confirmLoading.value = true;
|
||||
const isUpdate = ref<boolean>(false);
|
||||
//时间格式化
|
||||
let model = formData;
|
||||
if (model.id) {
|
||||
isUpdate.value = true;
|
||||
}
|
||||
//循环数据
|
||||
for (let data in model) {
|
||||
//如果该数据是数组并且是字符串类型
|
||||
if (model[data] instanceof Array) {
|
||||
let valueType = getValueType(formRef.value.getProps, data);
|
||||
//如果是字符串类型的需要变成以逗号分割的字符串
|
||||
if (valueType === 'string') {
|
||||
model[data] = model[data].join(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
await saveOrUpdate(model, isUpdate.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
createMessage.success(res.message);
|
||||
emit('ok');
|
||||
} else {
|
||||
createMessage.warning(res.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
confirmLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
add,
|
||||
edit,
|
||||
submitForm,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.antd-modal-form {
|
||||
min-height: 500px !important;
|
||||
overflow-y: auto;
|
||||
padding: 24px 24px 24px 24px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<a-modal :title="title" :width="width" :visible="visible" @ok="handleOk" :okButtonProps="{ class: { 'jee-hidden': disableSubmit } }" @cancel="handleCancel" cancelText="关闭">
|
||||
<PjxxJszbpjfForm ref="registerForm" @ok="submitCallback" :formDisabled="disableSubmit" :formBpm="false"></PjxxJszbpjfForm>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick, defineExpose } from 'vue';
|
||||
import PjxxJszbpjfForm from './PjxxJszbpjfForm.vue'
|
||||
|
||||
const title = ref<string>('');
|
||||
const width = ref<number>(800);
|
||||
const visible = ref<boolean>(false);
|
||||
const disableSubmit = ref<boolean>(false);
|
||||
const registerForm = ref();
|
||||
const emit = defineEmits(['register', 'success']);
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*/
|
||||
function add() {
|
||||
title.value = '新增';
|
||||
visible.value = true;
|
||||
nextTick(() => {
|
||||
registerForm.value.add();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑
|
||||
* @param record
|
||||
*/
|
||||
function edit(record) {
|
||||
title.value = disableSubmit.value ? '详情' : '编辑';
|
||||
visible.value = true;
|
||||
nextTick(() => {
|
||||
registerForm.value.edit(record);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定按钮点击事件
|
||||
*/
|
||||
function handleOk() {
|
||||
registerForm.value.submitForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* form保存回调事件
|
||||
*/
|
||||
function submitCallback() {
|
||||
handleCancel();
|
||||
emit('success');
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消按钮回调事件
|
||||
*/
|
||||
function handleCancel() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
add,
|
||||
edit,
|
||||
disableSubmit,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/**隐藏样式-modal确定按钮 */
|
||||
.jee-hidden {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
|
@ -157,7 +157,6 @@ function toIndex(){
|
|||
if(roleList){
|
||||
for(var i=0;i<roleList.length;i++){
|
||||
var roleCode = roleList[i].roleCode
|
||||
console.log(`🚀 ~ onMounted ~ roleCode:`, roleCode)
|
||||
if(roleCode == 'admin' || roleCode == 'jwms'){
|
||||
sfxx.value = "1";
|
||||
}
|
||||
|
|
|
@ -152,7 +152,6 @@ onMounted(() => {
|
|||
if (roleList) {
|
||||
for (var i = 0; i < roleList.length; i++) {
|
||||
var roleCode = roleList[i].roleCode;
|
||||
console.log(`🚀 ~ onMounted ~ roleCode:`, roleCode);
|
||||
if (roleCode == 'admin' || roleCode == 'jwms') {
|
||||
sfxx.value = '1';
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<a-card class="rowGutter" id="ktsbDom">
|
||||
<template #title>
|
||||
<span class="titleName">听课笔记</span>
|
||||
<span class="titleDownload"><a @click="downloadByUrl({url:'/downPath/tkjlb.docx',target: '_self',fileName:'东北师范大学听课记录表.docx'})">一般听课评价表</a></span>
|
||||
<!-- <span class="titleDownload"><a @click="downloadByUrl({url:'/downPath/tkjlb.docx',target: '_self',fileName:'东北师范大学听课记录表.docx'})">一般听课评价表</a></span>
|
||||
<span class="titleDownload"><a @click="downloadByUrl({url:'/downPath/jxzlpjb.docx',target: '_self',fileName:'东北师范大学本科课堂教学质量评价表(同行专家用)'})">同行听课评价表</a></span>
|
||||
<span class="titleDownload"><a @click="downloadByUrl({url:'/downPath/szkcpjb.docx',target: '_self',fileName:'东北师范大学政课评价表'})">思政课评价表</a></span>
|
||||
<span class="titleDownload"><a @click="downloadByUrl({url:'/downPath/tkpjb.docx',target: '_self',fileName:'线上教学听课评价表-新.docx'})">线上课评价表</a></span>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<span>登录后教师可以预约课程,听课完成后可以对课程进行评价,评价有助于快速完成本学期的听课任务</span>
|
||||
</template>
|
||||
<span class="helpClass">?</span>
|
||||
</a-tooltip></span>
|
||||
</a-tooltip></span> -->
|
||||
<span style="float:right;">
|
||||
<a style="padding-right: 0.5rem;" @click="addTkbj()">新增笔记</a>
|
||||
<RouterLink to="/site/ktsbMore">查看更多</RouterLink>
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
</div>
|
||||
<list :queryParam="{ pageSize: 4, ...tkzjParam }" style="max-height: 106px;"/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" tab="学生评教结果">
|
||||
<!-- <a-tab-pane key="2" tab="学生评教结果">
|
||||
<studentPjjgList :queryParam="{ pageSize: 4, ...tkzjParam }" style="max-height: 106px;"/>
|
||||
</a-tab-pane> -->
|
||||
<a-tab-pane key="3" tab="学生评教结果">
|
||||
<JszbpjfList :queryParam="{ pageSize: 4, ...tkzjParam }" style="max-height: 106px;"/>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-card>
|
||||
|
@ -22,6 +25,7 @@
|
|||
import { ref } from 'vue';
|
||||
import list from '/@/views/site/pjjgPage/list.vue';
|
||||
import studentPjjgList from '/@/views/site/pjjgPage/studentPjjgList.vue';
|
||||
import JszbpjfList from '/@/views/kc/pjxxJszbpjf/JszbpjfList.vue';
|
||||
import { nextTick } from 'vue';
|
||||
|
||||
const tkzjParam = ref({});
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</a-row>
|
||||
</div>
|
||||
</a-card>
|
||||
<!-- <a-card>
|
||||
<a-card>
|
||||
<div>
|
||||
<span style="float: left; line-height: 30px; font-size: 18px; font-weight: bold">教学大纲:</span>
|
||||
<span style="width: 300px; float: left">
|
||||
|
@ -118,7 +118,7 @@
|
|||
<span style="float: left">往届学生评价</span>
|
||||
</div>
|
||||
<studentPjjgTeaList :queryParam="{ pageSize: 3, ...tkzjParam, kcbh: '' }" style="max-height: 106px" />
|
||||
</a-card> -->
|
||||
</a-card>
|
||||
</div>
|
||||
<div v-if="!showYl">
|
||||
<div>
|
||||
|
@ -154,6 +154,7 @@ import { useGlobSetting } from '/@/hooks/setting';
|
|||
import studentZyxxModal from '/@/views/site/studentWdkc/studentZyxxModal.vue';
|
||||
import JEditor from '/@/components/Form/src/jeecg/components/JEditor.vue';
|
||||
import studentZyxx from '/@/views/site/studentWdkc/studentZyxx.vue';
|
||||
import { encryptByBase64 } from '/@/utils/cipher';
|
||||
|
||||
const globSetting = useGlobSetting();
|
||||
const baseApiUrl = globSetting.domainUrl;
|
||||
|
@ -279,23 +280,30 @@ async function handleDelete(record) {
|
|||
}
|
||||
|
||||
function openPdf(record) {
|
||||
if (record.pdfPath) {
|
||||
var url2 = getFileAccessHttpUrl(record.pdfPath);
|
||||
let url = baseApiUrl + '/generic/web/viewer.html?file=' + encodeURIComponent(url2);
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
createMessage.warning('暂无文件或文件上传中');
|
||||
}
|
||||
var file = baseApiUrl + "/"+record.filePath;
|
||||
console.log('🤬', file);
|
||||
window.open('https://jxdd.nenu.edu.cn/onlinePreview/onlinePreview?url=' + encodeURIComponent(encryptByBase64(file)));
|
||||
|
||||
// if (record.pdfPath) {
|
||||
// var url2 = getFileAccessHttpUrl(record.pdfPath);
|
||||
// let url = baseApiUrl + '/generic/web/viewer.html?file=' + encodeURIComponent(url2);
|
||||
// window.open(url, '_blank');
|
||||
// } else {
|
||||
// createMessage.warning('暂无文件或文件上传中');
|
||||
// }
|
||||
}
|
||||
|
||||
function openJxrlPdf(record) {
|
||||
if (record.jxrlPdfPath) {
|
||||
var url2 = getFileAccessHttpUrl(record.jxrlPdfPath);
|
||||
let url = baseApiUrl + '/generic/web/viewer.html?file=' + encodeURIComponent(url2);
|
||||
window.open(url, '_blank');
|
||||
} else {
|
||||
createMessage.warning('暂无文件或文件上传中');
|
||||
}
|
||||
var file = baseApiUrl + "/"+record.filePath;
|
||||
console.log('🤬', file);
|
||||
window.open('https://jxdd.nenu.edu.cn/onlinePreview/onlinePreview?url=' + encodeURIComponent(encryptByBase64(file)));
|
||||
// if (record.jxrlPdfPath) {
|
||||
// var url2 = getFileAccessHttpUrl(record.jxrlPdfPath);
|
||||
// let url = baseApiUrl + '/generic/web/viewer.html?file=' + encodeURIComponent(url2);
|
||||
// window.open(url, '_blank');
|
||||
// } else {
|
||||
// createMessage.warning('暂无文件或文件上传中');
|
||||
// }
|
||||
}
|
||||
|
||||
//常见问题回调刷新
|
||||
|
|
|
@ -21,12 +21,6 @@
|
|||
</template>
|
||||
<span >课程简介</span>
|
||||
</a-menu-item >
|
||||
<!-- <a-menu-item key="sub2">
|
||||
<template #icon>
|
||||
<SettingOutlined />
|
||||
</template>
|
||||
<span @click="getGzt('jxdg')">教学大纲</span>
|
||||
</a-menu-item > -->
|
||||
<a-menu-item key="sub3" @click="getGzt('dqzy')">
|
||||
<template #icon>
|
||||
<SnippetsOutlined />
|
||||
|
@ -50,13 +44,24 @@
|
|||
<span>课程资源</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="sub7" @click="getGzt('khcl')">
|
||||
<BlockOutlined />
|
||||
<Icon icon="ant-design:import-outlined" />
|
||||
<span>上传考核材料</span>
|
||||
</a-menu-item>
|
||||
<!-- <a-menu-item key="sub8">
|
||||
<BlockOutlined />
|
||||
<span @click="gotoPageByName('jiaoXueDanYuanNeiRong')">教学单元</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="sub8" @click="gotoPageByName('jiaoXueDanYuanNeiRong')">
|
||||
<Icon icon="ant-design:fund-projection-screen-outlined" />
|
||||
<span >教学单元</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="sub9" @click="getGzt('tlq')">
|
||||
<Icon icon="ant-design:aliwangwang-outlined" />
|
||||
<span >讨论区</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="sub10" @click="getGzt('jxdg')">
|
||||
<template #icon>
|
||||
<SettingOutlined />
|
||||
</template>
|
||||
<span>教学大纲</span>
|
||||
</a-menu-item >
|
||||
<!--
|
||||
<a-menu-item key="sub9">
|
||||
<BlockOutlined />
|
||||
<span @click="getGzt('dcwj')">问卷调查</span>
|
||||
|
@ -65,10 +70,6 @@
|
|||
<BlockOutlined />
|
||||
<span @click="getGzt('yiykzyk')">教学资源库</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="sub11">
|
||||
<BlockOutlined />
|
||||
<span @click="getGzt('tlq')">讨论区</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item key="sub12">
|
||||
<BlockOutlined />
|
||||
<span @click="getGzt('gongju')">AI识别出勤率</span>
|
||||
|
|
|
@ -21,12 +21,6 @@
|
|||
</template>
|
||||
<span>课程简介</span>
|
||||
</a-menu-item>
|
||||
<!-- <a-menu-item key="sub3">
|
||||
<template #icon>
|
||||
<SettingOutlined />
|
||||
</template>
|
||||
<span @click="getGzt('jxdg')">教学大纲</span>
|
||||
</a-menu-item> -->
|
||||
<a-menu-item key="sub4" @click="getGzt('dqzy')">
|
||||
<template #icon>
|
||||
<SnippetsOutlined />
|
||||
|
@ -51,6 +45,28 @@
|
|||
</template>
|
||||
<span>课程资源</span>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item key="sub9" @click="getGzt('stuJiaoXueDanYuanNeiRong')">
|
||||
<template #icon>
|
||||
<Icon icon="ant-design:fund-projection-screen-outlined" />
|
||||
</template>
|
||||
<span>教学单元</span>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item key="sub10" @click="getGzt('tlq')">
|
||||
<template #icon>
|
||||
<Icon icon="ant-design:aliwangwang-outlined" />
|
||||
</template>
|
||||
<span >讨论区</span>
|
||||
</a-menu-item>
|
||||
|
||||
<a-menu-item key="sub11" @click="getGzt('jxdg')">
|
||||
<template #icon>
|
||||
<Icon icon="ant-design:aliwangwang-outlined" />
|
||||
</template>
|
||||
<span >教学大纲</span>
|
||||
</a-menu-item>
|
||||
|
||||
</a-menu>
|
||||
</a-col>
|
||||
<a-col :lg="0" :xs="{ span: 24 }" style="text-align: right;">
|
||||
|
|
|
@ -1,99 +1,260 @@
|
|||
<template>
|
||||
<a-alert :message="`当前时间: ${tday}`" />
|
||||
<a-calendar v-model:value="value" @panelChange="onPanelChange">
|
||||
<template #dateCellRender="{ current }">
|
||||
<ul class="events">
|
||||
<li v-for="item in getListData(current)" :key="item.content" @click="handleKecheng(item)">
|
||||
<a-badge status="success" :text="item.kcmc" />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<!-- 去掉头部年月切换 -->
|
||||
<template #headerRender="{ current }">
|
||||
</template>
|
||||
</a-calendar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, ref, onMounted } from 'vue';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { dateFormat } from '/@/utils/common/compUtils';
|
||||
|
||||
const value = ref<Dayjs>();
|
||||
const tday = ref<string>('');
|
||||
const studentKclist = ref<any>([]);
|
||||
const getListData = (value: Dayjs) => {
|
||||
//将当前日历循环到的日子格式化为yyyy-MM-dd
|
||||
const formattedDate = value.format('YYYY-MM-DD');
|
||||
if (formattedDate.substring(0, 7) == tday.value) {
|
||||
return studentKclist.value.filter(item => item.skrq === formattedDate);
|
||||
} else {
|
||||
return [];
|
||||
<div>
|
||||
<!-- <MyTemplate></MyTemplate> -->
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a-button @click="startConverting" v-if="yysbBoolean">开始转写</a-button>
|
||||
<a-button @click="endConverting" v-else>结束转写</a-button>
|
||||
<div v-if="resultText">
|
||||
{{ resultText }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<a-alert :message="`当前时间: ${tday}`" />
|
||||
<a-calendar v-model:value="value" @panelChange="onPanelChange">
|
||||
<template #dateCellRender="{ current }">
|
||||
<a-row>
|
||||
<a-col :xs="0" :md="24">
|
||||
<ul class="events" >
|
||||
<li v-for="item in getListData(current)" :key="item.content" @click="handleKecheng(item)" :title="item.kcmc+`-`+item.skjs">
|
||||
<a-badge status="success" :text="item.kcmc" />
|
||||
</li>
|
||||
</ul>
|
||||
</a-col>
|
||||
<a-col :xs="24" :md="0">
|
||||
<a-popover title="课程信息" v-if="getSfyk(current)">
|
||||
<template #content>
|
||||
<ul class="events" >
|
||||
<li v-for="item in getListData(current)" :key="item.content" @click="handleKecheng(item)" :title="item.kcmc+`-`+item.skjs">
|
||||
<a-badge status="success" :text="item.kcmc" />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
<a-avatar style="background-color: #1d9b64;width:20px;height: 20px;line-height: 20px;">有</a-avatar>
|
||||
</a-popover>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
|
||||
</template>
|
||||
<!-- 去掉头部年月切换 -->
|
||||
<template #headerRender="{ current }">
|
||||
</template>
|
||||
</a-calendar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineComponent, ref, onMounted } from 'vue';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
import { dateFormat } from '/@/utils/common/compUtils';
|
||||
|
||||
import { SpeechRecognizer } from '/@/views/site/studentWdkc/yysbUtils/app/speechrecognizer';
|
||||
import WebAudioSpeechRecognizer from '/@/views/site/studentWdkc/yysbUtils/app/webaudiospeechrecognizer';
|
||||
import * as ASR from '/@/views/site/studentWdkc/yysbUtils/examples/trtc/asr.esm';
|
||||
let config = {
|
||||
secretKey: 'QcbMitrb7QVMyNaZJp7E7mESSFrGUWKz',
|
||||
secretId: 'AKIDJfpnZ9EF5i7II4cw3xfARVMyNYeljU3Q',
|
||||
appId: 1255881128,
|
||||
}
|
||||
|
||||
const params = {
|
||||
signCallback: signCallback, // 鉴权函数,若直接使用默认鉴权函数。可不传此参数
|
||||
// 用户参数
|
||||
secretid: config.secretId,
|
||||
secretkey: config.secretKey,
|
||||
appid: config.appId,
|
||||
// 临时密钥参数,非必填
|
||||
// token: config.token,
|
||||
// 实时识别接口参数
|
||||
engine_model_type : '16k_zh', // 因为内置WebRecorder采样16k的数据,所以参数 engineModelType 需要选择16k的引擎,为 '16k_zh'
|
||||
// 以下为非必填参数,可跟据业务自行修改
|
||||
// voice_format : 1,
|
||||
// hotword_id : '08003a00000000000000000000000000',
|
||||
// needvad: 1,
|
||||
// filter_dirty: 1,
|
||||
// filter_modal: 2,
|
||||
// filter_punc: 0,
|
||||
// convert_num_mode : 1,
|
||||
// word_info: 2
|
||||
}
|
||||
};
|
||||
|
||||
const onPanelChange = (value: Dayjs) => {
|
||||
tday.value = dateFormat(value, "yyyy-MM");
|
||||
};
|
||||
//点击课程信息
|
||||
function handleKecheng(record) {
|
||||
console.log(`🚀 ~ openKecheng ~ record:`, record);
|
||||
var jgh = record.jgh.split(",")[0];
|
||||
// defHttp.post({ url: '/zyDbtx/zyDbtx/deleteByRwbhCreate', params: { rwbh: record.rwbh, fbr: jgh } }).then((res) => {
|
||||
// loaddata();
|
||||
// });
|
||||
var url = '/stuzy/StudentGonggaoList?rwbh=' + record.rwbh + '&xqxn=' + record.xnxq + "&teano=" + record.jgh;
|
||||
window.open(url, '_blank');
|
||||
const resultText = ref<string>('');
|
||||
const yysbBoolean = ref<boolean>(true);
|
||||
|
||||
|
||||
/** 获取签名 start */
|
||||
|
||||
function toUint8Array(wordArray) {
|
||||
// Shortcuts
|
||||
const words = wordArray.words;
|
||||
const sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
const u8 = new Uint8Array(sigBytes);
|
||||
for (let i = 0; i < sigBytes; i++) {
|
||||
u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
}
|
||||
return u8;
|
||||
}
|
||||
|
||||
//进入就加载
|
||||
onMounted(() => {
|
||||
tday.value = dateFormat(new Date(), "yyyy-MM");
|
||||
defHttp.get({ url: '/ktgl/kcKetangbiao/getStudentRiliKclist' }).then((res) => {
|
||||
res.forEach(element => {
|
||||
studentKclist.value.push(element)
|
||||
function Uint8ArrayToString(fileData){
|
||||
let dataString = '';
|
||||
for (let i = 0; i < fileData.length; i++) {
|
||||
dataString += String.fromCharCode(fileData[i]);
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
// 签名函数示例
|
||||
function signCallback(signStr) {
|
||||
const secretKey = config.secretKey;
|
||||
const hash = window.CryptoJSTest.HmacSHA1(signStr, secretKey);
|
||||
const bytes = Uint8ArrayToString(toUint8Array(hash));
|
||||
return window.btoa(bytes);
|
||||
}
|
||||
|
||||
/** 获取签名 end */
|
||||
|
||||
|
||||
let webAudioSpeechRecognizer = null;
|
||||
function startConverting() {
|
||||
var t = ASR.guid();
|
||||
console.log(t);
|
||||
webAudioSpeechRecognizer = new WebAudioSpeechRecognizer(params);
|
||||
console.log("🚀 ~ 1111 ~ con:", webAudioSpeechRecognizer)
|
||||
|
||||
// 开始识别
|
||||
webAudioSpeechRecognizer.OnRecognitionStart = (res) => {
|
||||
console.log('开始识别', res);
|
||||
};
|
||||
|
||||
// 识别变化时
|
||||
webAudioSpeechRecognizer.OnRecognitionResultChange = (res) => {
|
||||
console.log('识别变化时', res);
|
||||
const currentText = res.result.voice_text_str;
|
||||
console.log("🚀 ~ startConverting ~ currentText:", currentText)
|
||||
resultText.value = currentText;
|
||||
};
|
||||
|
||||
// 识别结束
|
||||
webAudioSpeechRecognizer.OnRecognitionComplete = (res) => {
|
||||
console.log('识别结束', res);
|
||||
};
|
||||
|
||||
|
||||
webAudioSpeechRecognizer.start();
|
||||
yysbBoolean.value = false;
|
||||
|
||||
}
|
||||
|
||||
function endConverting(){
|
||||
webAudioSpeechRecognizer.stop();
|
||||
|
||||
// 识别结束
|
||||
webAudioSpeechRecognizer.OnRecognitionComplete = (res) => {
|
||||
console.log('识别结束', res);
|
||||
};
|
||||
yysbBoolean.value = true;
|
||||
webAudioSpeechRecognizer = null;
|
||||
}
|
||||
|
||||
const value = ref<Dayjs>();
|
||||
const tday = ref<string>('');
|
||||
const studentKclist = ref<any>([]);
|
||||
const getListData = (value: Dayjs) => {
|
||||
//将当前日历循环到的日子格式化为yyyy-MM-dd
|
||||
const formattedDate = value.format('YYYY-MM-DD');
|
||||
if (formattedDate.substring(0, 7) == tday.value) {
|
||||
return studentKclist.value.filter(item => item.skrq === formattedDate);
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
//判断是否有课
|
||||
const getSfyk = (value: Dayjs) => {
|
||||
//将当前日历循环到的日子格式化为yyyy-MM-dd
|
||||
const formattedDate = value.format('YYYY-MM-DD');
|
||||
if (formattedDate.substring(0, 7) == tday.value) {
|
||||
var listSfyk = studentKclist.value.filter(item => item.skrq === formattedDate)
|
||||
if(listSfyk.length>0){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPanelChange = (value: Dayjs) => {
|
||||
tday.value = dateFormat(value, "yyyy-MM");
|
||||
getRiliList(tday.value)
|
||||
};
|
||||
//点击课程信息
|
||||
function handleKecheng(record) {
|
||||
var jgh = record.jgh.split(",")[0];
|
||||
// defHttp.post({ url: '/zyDbtx/zyDbtx/deleteByRwbhCreate', params: { rwbh: record.rwbh, fbr: jgh } }).then((res) => {
|
||||
// loaddata();
|
||||
// });
|
||||
var url = '/stuzy/StudentGonggaoList?rwbh=' + record.rwbh + '&xqxn=' + record.xnxq + "&teano=" + record.jgh;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
//获取授课信息
|
||||
function getRiliList(skrq){
|
||||
studentKclist.value = []
|
||||
defHttp.get({ url: '/ktgl/kcKetangbiao/getStudentRiliKclist' ,params:{skrq}}).then((res) => {
|
||||
res.forEach(element => {
|
||||
studentKclist.value.push(element)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//进入就加载
|
||||
onMounted(() => {
|
||||
tday.value = dateFormat(new Date(), "yyyy-MM");
|
||||
getRiliList(tday.value)
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.events {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.events li {
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
/* 鼠标指针变为小手 */
|
||||
transition: background-color 0.3s;
|
||||
/* 平滑的背景颜色变化 */
|
||||
}
|
||||
|
||||
.events li:hover {
|
||||
background-color: #a3a3a3;
|
||||
/* 深灰色背景 */
|
||||
color: white;
|
||||
/* 如果需要的话,可以改变文字颜色以保证可读性 */
|
||||
}
|
||||
|
||||
.events .ant-badge-status {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.notes-month {
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.notes-month section {
|
||||
font-size: 28px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
.events {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.events li {
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
/* 鼠标指针变为小手 */
|
||||
transition: background-color 0.3s;
|
||||
/* 平滑的背景颜色变化 */
|
||||
}
|
||||
|
||||
.events li:hover {
|
||||
background-color: #a3a3a3;
|
||||
/* 深灰色背景 */
|
||||
color: white;
|
||||
/* 如果需要的话,可以改变文字颜色以保证可读性 */
|
||||
}
|
||||
|
||||
.events .ant-badge-status {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.notes-month {
|
||||
text-align: center;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.notes-month section {
|
||||
font-size: 28px;
|
||||
}
|
||||
</style>
|
|
@ -3,15 +3,15 @@
|
|||
<div style="margin-top: 20px">
|
||||
<span style="margin-left: 30px; font-size: 24px; font-weight: 600">我的课程</span>
|
||||
</div>
|
||||
<!-- <div>
|
||||
<studentRili></studentRili>
|
||||
</div> -->
|
||||
|
||||
|
||||
<div style="text-align: right; width: 100%; padding-right:20px;margin-top:30px">
|
||||
<a-switch style="margin-top:-60px" v-model:checked="checked1" checkedChildren="卡片" unCheckedChildren="列表" />
|
||||
<a-switch style="margin-top:-60px" v-model:checked="checked1" checkedChildren="日历" unCheckedChildren="卡片" />
|
||||
</div>
|
||||
<div v-if="checked1">
|
||||
<studentRili></studentRili>
|
||||
|
||||
</div>
|
||||
<div v-if="!checked1">
|
||||
<div style="min-height: 200px">
|
||||
<a-row>
|
||||
<a-col
|
||||
|
@ -68,9 +68,8 @@
|
|||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!checked1">
|
||||
<div style="min-height: 200px">
|
||||
|
||||
<!-- <div style="min-height: 200px">
|
||||
<a-row>
|
||||
<a-col :span="24" style="padding: 0 10px; margin-bottom: 10px" v-for="(item, index2) in dataSource" :key="index2">
|
||||
<a-row>
|
||||
|
@ -87,7 +86,7 @@
|
|||
<a-empty />
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
</a-row>
|
||||
</div>
|
||||
</a-card>
|
||||
<!-- <a-card>
|
||||
<a-card>
|
||||
<div>
|
||||
<span style="float: left;line-height: 30px; font-size: 18px; font-weight: bold;">教学大纲:</span>
|
||||
<span style="width:300px;float: left;margin-top: 3px;">
|
||||
|
@ -60,7 +60,7 @@
|
|||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
<a-card>
|
||||
<!-- <a-card>
|
||||
<div style="line-height: 30px; font-size: 18px; font-weight: bold;width:100%;">
|
||||
<span style="float: left;">往届学生评价</span>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
import '../examples/lib/cryptojs.js';
|
||||
|
||||
// 识别需要过滤的参数
|
||||
const needFiltrationParams = ['appid', 'secretkey', 'signCallback', 'echoCancellation'];
|
||||
|
||||
function formatSignString(query, params){
|
||||
let strParam = "";
|
||||
let signStr = "asr.cloud.tencent.com/asr/v2/";
|
||||
if(query['appid']){
|
||||
signStr += query['appid'];
|
||||
}
|
||||
const keys = Object.keys(params);
|
||||
keys.sort();
|
||||
for (let i = 0, len = keys.length; i < len; i++) {
|
||||
strParam += `&${keys[i]}=${params[keys[i]]}`;
|
||||
}
|
||||
return `${signStr}?${strParam.slice(1)}`;
|
||||
}
|
||||
async function createQuery(query){
|
||||
let params = {};
|
||||
const time = new Date().getTime();
|
||||
|
||||
async function getServerTime(){
|
||||
return new Promise((resolve, reject)=>{
|
||||
try {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", 'https://asr.cloud.tencent.com/server_time', true);
|
||||
xhr.send();
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4 && xhr.status === 200) {
|
||||
resolve(xhr.responseText);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
})
|
||||
}
|
||||
const serverTime = await getServerTime();
|
||||
params['secretid'] = query.secretid || '';
|
||||
params['engine_model_type'] = query.engine_model_type || '16k_zh';
|
||||
params['timestamp'] = parseInt(serverTime) || Math.round(time / 1000);
|
||||
params['expired'] = Math.round(time / 1000) + 24 * 60 * 60;
|
||||
params['nonce'] = Math.round(time / 100000);
|
||||
params['voice_id'] = guid();
|
||||
params['voice_format'] = query.voice_format || 1;
|
||||
|
||||
const tempQuery = { ...query };
|
||||
for (let i = 0, len = needFiltrationParams.length; i < len; i++) {
|
||||
if (tempQuery.hasOwnProperty(needFiltrationParams[i])) {
|
||||
delete tempQuery[needFiltrationParams[i]];
|
||||
}
|
||||
}
|
||||
|
||||
params = {
|
||||
...tempQuery,
|
||||
...params,
|
||||
};
|
||||
return params;
|
||||
}
|
||||
|
||||
export const guid = () => {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = Math.random() * 16 | 0,
|
||||
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
};
|
||||
// 获取签名原文
|
||||
async function getUrl(self, params) {
|
||||
if (!params.appid || !params.secretid) {
|
||||
self.isLog && console.log(self.requestId, '请确认是否填入账号信息', TAG);
|
||||
self.OnError('请确认是否填入账号信息');
|
||||
return false;
|
||||
}
|
||||
const urlQuery = await createQuery(params);
|
||||
const queryStr = formatSignString(params, urlQuery);
|
||||
let signature = '';
|
||||
if (params.signCallback) {
|
||||
signature = params.signCallback(queryStr);
|
||||
} else {
|
||||
signature = signCallback(params.secretkey, queryStr);
|
||||
}
|
||||
return `wss://${queryStr}&signature=${encodeURIComponent(signature)}`;
|
||||
}
|
||||
/** 获取签名 start */
|
||||
|
||||
function toUint8Array(wordArray) {
|
||||
// Shortcuts
|
||||
const words = wordArray.words;
|
||||
const sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
const u8 = new Uint8Array(sigBytes);
|
||||
for (let i = 0; i < sigBytes; i++) {
|
||||
u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
}
|
||||
return u8;
|
||||
}
|
||||
|
||||
function Uint8ArrayToString(fileData){
|
||||
let dataString = '';
|
||||
for (let i = 0; i < fileData.length; i++) {
|
||||
dataString += String.fromCharCode(fileData[i]);
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
// 签名函数示例
|
||||
function signCallback(secretKey, signStr) {
|
||||
const hash = window.CryptoJSTest.HmacSHA1(signStr, secretKey);
|
||||
const bytes = Uint8ArrayToString(toUint8Array(hash));
|
||||
return window.btoa(bytes);
|
||||
}
|
||||
|
||||
/** 获取签名 end */
|
||||
|
||||
const TAG = 'SpeechRecognizer';
|
||||
export class SpeechRecognizer {
|
||||
constructor(params, requestId, isLog) {
|
||||
this.socket = null;
|
||||
this.isSignSuccess = false; // 是否鉴权成功
|
||||
this.isSentenceBegin = false; // 是否一句话开始
|
||||
this.query = {
|
||||
...params
|
||||
};
|
||||
this.isRecognizeComplete = false; // 当前是否识别结束
|
||||
this.requestId = requestId;
|
||||
this.isLog = isLog;
|
||||
this.sendCount = 0;
|
||||
this.getMessageList = [];
|
||||
}
|
||||
// 暂停识别,关闭连接
|
||||
stop() {
|
||||
if (this.socket && this.socket.readyState === 1) {
|
||||
this.socket.send(JSON.stringify({type: 'end'}));
|
||||
this.isRecognizeComplete = true;
|
||||
} else {
|
||||
// this.OnError({ code : 6003, message: '连接未建立或连接已关闭' });
|
||||
if (this.socket && this.socket.readyState === 1) {
|
||||
this.socket.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 建立websocket链接 data 为用户收集的音频数据
|
||||
async start(){
|
||||
this.socket = null;
|
||||
this.getMessageList = [];
|
||||
const url = await getUrl(this, this.query);
|
||||
if (!url) {
|
||||
this.isLog && console.log(this.requestId, '鉴权失败', TAG);
|
||||
this.OnError('鉴权失败');
|
||||
return
|
||||
}
|
||||
this.isLog && console.log(this.requestId, 'get ws url', url, TAG);
|
||||
if ('WebSocket' in window) {
|
||||
this.socket = new WebSocket(url);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
this.socket = new MozWebSocket(url);
|
||||
} else {
|
||||
this.isLog && console.log(this.requestId, '浏览器不支持WebSocket', TAG);
|
||||
this.OnError('浏览器不支持WebSocket');
|
||||
return
|
||||
}
|
||||
this.socket.onopen = (e) => { // 连接建立时触发
|
||||
this.isLog && console.log(this.requestId, '连接建立', e, TAG);
|
||||
};
|
||||
this.socket.onmessage = async (e) => { // 连接建立时触发
|
||||
try {
|
||||
this.getMessageList.push(JSON.stringify(e));
|
||||
const response = JSON.parse(e.data);
|
||||
if (response.code !== 0) {
|
||||
if (this.socket.readyState === 1) {
|
||||
this.socket.close();
|
||||
}
|
||||
this.isLog && console.log(this.requestId, JSON.stringify(response), TAG);
|
||||
this.OnError(response);
|
||||
} else {
|
||||
if (!this.isSignSuccess) {
|
||||
this.OnRecognitionStart(response);
|
||||
this.isSignSuccess = true;
|
||||
}
|
||||
if (response.final === 1) {
|
||||
this.OnRecognitionComplete(response);
|
||||
return;
|
||||
}
|
||||
if (response.result) {
|
||||
if (response.result.slice_type === 0) {
|
||||
this.OnSentenceBegin(response);
|
||||
this.isSentenceBegin = true;
|
||||
} else if (response.result.slice_type === 2) {
|
||||
if (!this.isSentenceBegin) {
|
||||
this.OnSentenceBegin(response);
|
||||
}
|
||||
this.OnSentenceEnd(response);
|
||||
} else {
|
||||
this.OnRecognitionResultChange(response);
|
||||
}
|
||||
}
|
||||
this.isLog && console.log(this.requestId, response, TAG);
|
||||
}
|
||||
} catch (e) {
|
||||
this.isLog && console.log(this.requestId, 'socket.onmessage catch error', JSON.stringify(e), TAG);
|
||||
}
|
||||
|
||||
};
|
||||
this.socket.onerror = (e) => { // 通信发生错误时触发
|
||||
this.isLog && console.log(this.requestId, 'socket error callback', e, TAG);
|
||||
this.socket.close();
|
||||
this.OnError(e);
|
||||
}
|
||||
this.socket.onclose = (event) => {
|
||||
try {
|
||||
if (!this.isRecognizeComplete) {
|
||||
this.isLog && console.log(this.requestId, 'socket is close and error', JSON.stringify(event), TAG);
|
||||
this.OnError(event);
|
||||
}
|
||||
} catch (e) {
|
||||
this.isLog && console.log(this.requestId, 'socket is onclose catch' + this.sendCount, JSON.stringify(e), TAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
close() {
|
||||
this.socket && this.socket.readyState === 1 && this.socket.close(1000);
|
||||
}
|
||||
// 发送数据
|
||||
write(data) {
|
||||
try {
|
||||
if (!this.socket || String(this.socket.readyState) !== '1') {
|
||||
setTimeout(() => {
|
||||
if (this.socket && this.socket.readyState === 1) {
|
||||
this.socket.send(data);
|
||||
}
|
||||
}, 100);
|
||||
return false;
|
||||
}
|
||||
this.sendCount += 1;
|
||||
this.socket.send(data);
|
||||
} catch (e) {
|
||||
this.isLog && console.log(this.requestId , '发送数据 error catch', e, TAG);
|
||||
}
|
||||
};
|
||||
// 开始识别的时候
|
||||
OnRecognitionStart(res) {
|
||||
|
||||
}
|
||||
// 一句话开始的时候
|
||||
OnSentenceBegin(res) {
|
||||
|
||||
}
|
||||
// 识别结果发生变化的时候
|
||||
OnRecognitionResultChange() {
|
||||
|
||||
}
|
||||
// 一句话结束的时候
|
||||
OnSentenceEnd() {
|
||||
|
||||
}
|
||||
// 识别结束的时候
|
||||
OnRecognitionComplete() {
|
||||
|
||||
}
|
||||
// 识别失败
|
||||
OnError() {
|
||||
|
||||
}
|
||||
}
|
||||
typeof window !== 'undefined' && (window.SpeechRecognizer = SpeechRecognizer);
|
|
@ -0,0 +1,110 @@
|
|||
import WebRecorder from "./webrecorder.js";
|
||||
import { SpeechRecognizer, guid } from "./speechrecognizer.js";
|
||||
|
||||
export default class WebAudioSpeechRecognizer {
|
||||
constructor(params, isLog) {
|
||||
this.params = params;
|
||||
this.recorder = null;
|
||||
this.speechRecognizer = null;
|
||||
this.isCanSendData = false;
|
||||
this.isNormalEndStop = false;
|
||||
this.audioData = [];
|
||||
this.isLog = isLog;
|
||||
this.requestId = null;
|
||||
}
|
||||
start() {
|
||||
try {
|
||||
this.isLog && console.log('start function is click');
|
||||
this.requestId = guid();
|
||||
this.recorder = new WebRecorder(this.requestId, this.params, this.isLog);
|
||||
this.recorder.OnReceivedData = (data) => {
|
||||
if (this.isCanSendData) {
|
||||
this.speechRecognizer && this.speechRecognizer.write(data);
|
||||
}
|
||||
};
|
||||
// 录音失败时
|
||||
this.recorder.OnError = (err) => {
|
||||
this.speechRecognizer && this.speechRecognizer.close();
|
||||
this.stop();
|
||||
this.OnError(err);
|
||||
}
|
||||
this.recorder.OnStop = (res) => {
|
||||
if (this.speechRecognizer) {
|
||||
this.speechRecognizer.stop();
|
||||
// this.speechRecognizer = null;
|
||||
}
|
||||
this.OnRecorderStop(res);
|
||||
}
|
||||
this.recorder.start();
|
||||
if (!this.speechRecognizer) {
|
||||
this.speechRecognizer = new SpeechRecognizer(this.params, this.requestId, this.isLog);
|
||||
}
|
||||
// 开始识别
|
||||
this.speechRecognizer.OnRecognitionStart = (res) => {
|
||||
if (this.recorder) { // 录音正常
|
||||
this.OnRecognitionStart(res);
|
||||
this.isCanSendData = true;
|
||||
} else {
|
||||
this.speechRecognizer && this.speechRecognizer.close();
|
||||
}
|
||||
};
|
||||
// 一句话开始
|
||||
this.speechRecognizer.OnSentenceBegin = (res) => {
|
||||
this.OnSentenceBegin(res);
|
||||
};
|
||||
// 识别变化时
|
||||
this.speechRecognizer.OnRecognitionResultChange = (res) => {
|
||||
this.OnRecognitionResultChange(res);
|
||||
};
|
||||
// 一句话结束
|
||||
this.speechRecognizer.OnSentenceEnd = (res) => {
|
||||
this.OnSentenceEnd(res);
|
||||
};
|
||||
// 识别结束
|
||||
this.speechRecognizer.OnRecognitionComplete = (res) => {
|
||||
this.OnRecognitionComplete(res);
|
||||
this.isCanSendData = false;
|
||||
this.isNormalEndStop = true;
|
||||
};
|
||||
// 识别错误
|
||||
this.speechRecognizer.OnError = (res) => {
|
||||
if (this.speechRecognizer && !this.isNormalEndStop) {
|
||||
this.OnError(res);
|
||||
}
|
||||
this.speechRecognizer = null;
|
||||
this.recorder && this.recorder.stop();
|
||||
this.isCanSendData = false;
|
||||
};
|
||||
// 建立连接
|
||||
this.speechRecognizer.start();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
this.isLog && console.log('stop function is click');
|
||||
if (this.recorder) {
|
||||
this.recorder.stop();
|
||||
}
|
||||
}
|
||||
destroyStream() {
|
||||
this.isLog && console.log('destroyStream function is click', this.recorder);
|
||||
if (this.recorder) {
|
||||
this.recorder.destroyStream();
|
||||
}
|
||||
}
|
||||
// 开始识别的时候
|
||||
OnRecognitionStart(res) {}
|
||||
// 一句话开始的时候
|
||||
OnSentenceBegin(res) {}
|
||||
// 识别结果发生变化的时候
|
||||
OnRecognitionResultChange(res) {}
|
||||
// 一句话结束的时候
|
||||
OnSentenceEnd(res) {}
|
||||
// 识别结束的时候
|
||||
OnRecognitionComplete(res) {}
|
||||
// 识别失败
|
||||
OnError() {}
|
||||
OnRecorderStop() {}
|
||||
};
|
||||
typeof window !== 'undefined' && (window.WebAudioSpeechRecognizer = WebAudioSpeechRecognizer);
|
|
@ -0,0 +1,286 @@
|
|||
export function to16BitPCM(input) {
|
||||
const dataLength = input.length * (16 / 8);
|
||||
const dataBuffer = new ArrayBuffer(dataLength);
|
||||
const dataView = new DataView(dataBuffer);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < input.length; i++, offset += 2) {
|
||||
const s = Math.max(-1, Math.min(1, input[i]));
|
||||
dataView.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
|
||||
}
|
||||
return dataView;
|
||||
}
|
||||
export function to16kHz(audioData, sampleRate= 44100) {
|
||||
const data = new Float32Array(audioData);
|
||||
const fitCount = Math.round(data.length * (16000 / sampleRate));
|
||||
const newData = new Float32Array(fitCount);
|
||||
const springFactor = (data.length - 1) / (fitCount - 1);
|
||||
newData[0] = data[0];
|
||||
for (let i = 1; i < fitCount - 1; i++) {
|
||||
const tmp = i * springFactor;
|
||||
const before = Math.floor(tmp).toFixed();
|
||||
const after = Math.ceil(tmp).toFixed();
|
||||
const atPoint = tmp - before;
|
||||
newData[i] = data[before] + (data[after] - data[before]) * atPoint;
|
||||
}
|
||||
newData[fitCount - 1] = data[data.length - 1];
|
||||
return newData;
|
||||
}
|
||||
|
||||
const audioWorkletCode = `
|
||||
class MyProcessor extends AudioWorkletProcessor {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.audioData = [];
|
||||
this.sampleCount = 0;
|
||||
this.bitCount = 0;
|
||||
this.preTime = 0;
|
||||
}
|
||||
|
||||
process(inputs) {
|
||||
// 去处理音频数据
|
||||
// eslint-disable-next-line no-undef
|
||||
if (inputs[0][0]) {
|
||||
const output = ${to16kHz}(inputs[0][0], sampleRate);
|
||||
this.sampleCount += 1;
|
||||
const audioData = ${to16BitPCM}(output);
|
||||
this.bitCount += 1;
|
||||
const data = [...new Int8Array(audioData.buffer)];
|
||||
this.audioData = this.audioData.concat(data);
|
||||
if (new Date().getTime() - this.preTime > 100) {
|
||||
this.port.postMessage({
|
||||
audioData: new Int8Array(this.audioData),
|
||||
sampleCount: this.sampleCount,
|
||||
bitCount: this.bitCount
|
||||
});
|
||||
this.preTime = new Date().getTime();
|
||||
this.audioData = [];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('my-processor', MyProcessor);
|
||||
`;
|
||||
const TAG = 'WebRecorder';
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia
|
||||
|| navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
|
||||
|
||||
export default class WebRecorder {
|
||||
constructor(requestId, params, isLog) {
|
||||
this.audioData = [];
|
||||
this.allAudioData = [];
|
||||
this.stream = null;
|
||||
this.audioContext = null;
|
||||
this.requestId = requestId;
|
||||
this.frameTime = [];
|
||||
this.frameCount = 0;
|
||||
this.sampleCount = 0;
|
||||
this.bitCount = 0;
|
||||
this.mediaStreamSource = null;
|
||||
this.isLog = isLog;
|
||||
this.params = params;
|
||||
}
|
||||
static isSupportMediaDevicesMedia() {
|
||||
return !!(navigator.getUserMedia || (navigator.mediaDevices && navigator.mediaDevices.getUserMedia));
|
||||
}
|
||||
static isSupportUserMediaMedia() {
|
||||
return !!navigator.getUserMedia;
|
||||
}
|
||||
static isSupportAudioContext() {
|
||||
return typeof AudioContext !== 'undefined' || typeof webkitAudioContext !== 'undefined';
|
||||
}
|
||||
static isSupportMediaStreamSource(requestId, audioContext) {
|
||||
return typeof audioContext.createMediaStreamSource === 'function';
|
||||
}
|
||||
static isSupportAudioWorklet(audioContext) {
|
||||
return audioContext.audioWorklet && typeof audioContext.audioWorklet.addModule === 'function'
|
||||
&& typeof AudioWorkletNode !== 'undefined';
|
||||
}
|
||||
static isSupportCreateScriptProcessor(requestId, audioContext) {
|
||||
return typeof audioContext.createScriptProcessor === 'function';
|
||||
}
|
||||
start() {
|
||||
this.frameTime = [];
|
||||
this.frameCount = 0;
|
||||
this.allAudioData = [];
|
||||
this.audioData = [];
|
||||
this.sampleCount = 0;
|
||||
this.bitCount = 0;
|
||||
this.getDataCount = 0;
|
||||
this.audioContext = null;
|
||||
this.mediaStreamSource = null;
|
||||
this.stream = null;
|
||||
this.preTime = 0;
|
||||
try {
|
||||
if (WebRecorder.isSupportAudioContext()) {
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
} else {
|
||||
this.isLog && console.log(this.requestId, '浏览器不支持AudioContext', TAG);
|
||||
this.OnError('浏览器不支持AudioContext');
|
||||
}
|
||||
} catch (e) {
|
||||
this.isLog && console.log(this.requestId, '浏览器不支持webAudioApi相关接口', e, TAG);
|
||||
this.OnError('浏览器不支持webAudioApi相关接口');
|
||||
}
|
||||
this.getUserMedia(this.requestId, this.getAudioSuccess, this.getAudioFail);
|
||||
}
|
||||
stop() {
|
||||
if (!(/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent))){
|
||||
this.audioContext && this.audioContext.suspend();
|
||||
}
|
||||
this.audioContext && this.audioContext.suspend();
|
||||
this.isLog && console.log(this.requestId, `webRecorder stop ${this.sampleCount}/${this.bitCount}/${this.getDataCount}` , JSON.stringify(this.frameTime), TAG);
|
||||
this.OnStop(this.allAudioData);
|
||||
}
|
||||
destroyStream() {
|
||||
// 关闭通道
|
||||
if (this.stream) {
|
||||
this.stream.getTracks().map((val) => {
|
||||
val.stop();
|
||||
});
|
||||
this.stream = null;
|
||||
}
|
||||
}
|
||||
async getUserMedia(requestId, getStreamAudioSuccess, getStreamAudioFail) {
|
||||
let audioOption = {
|
||||
echoCancellation: true,
|
||||
};
|
||||
if (this.params && String(this.params.echoCancellation) === 'false') { // 关闭回声消除
|
||||
audioOption = {
|
||||
echoCancellation: false,
|
||||
};
|
||||
}
|
||||
const mediaOption = {
|
||||
audio: audioOption,
|
||||
video: false,
|
||||
};
|
||||
// 获取用户的麦克风
|
||||
if (WebRecorder.isSupportMediaDevicesMedia()) {
|
||||
navigator.mediaDevices
|
||||
.getUserMedia(mediaOption)
|
||||
.then(stream => {
|
||||
this.stream = stream;
|
||||
getStreamAudioSuccess.call(this, requestId, stream);
|
||||
})
|
||||
.catch(e => {
|
||||
getStreamAudioFail.call(this, requestId, e);
|
||||
});
|
||||
} else if (WebRecorder.isSupportUserMediaMedia()) {
|
||||
navigator.getUserMedia(mediaOption,
|
||||
stream => {
|
||||
this.stream = stream;
|
||||
getStreamAudioSuccess.call(this, requestId, stream);
|
||||
},
|
||||
function(err) {
|
||||
getStreamAudioFail.call(this, requestId, err);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (navigator.userAgent.toLowerCase().match(/chrome/) && location.origin.indexOf('https://') < 0) {
|
||||
this.isLog && console.log(this.requestId, 'chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限', TAG);
|
||||
this.OnError('chrome下获取浏览器录音功能,因为安全性问题,需要在localhost或127.0.0.1或https下才能获取权限');
|
||||
} else {
|
||||
this.isLog && console.log(this.requestId, '无法获取浏览器录音功能,请升级浏览器或使用chrome', TAG);
|
||||
this.OnError('无法获取浏览器录音功能,请升级浏览器或使用chrome');
|
||||
}
|
||||
this.audioContext && this.audioContext.close();
|
||||
}
|
||||
}
|
||||
async getAudioSuccess(requestId, stream) {
|
||||
if (!this.audioContext) {
|
||||
return false;
|
||||
}
|
||||
if (this.mediaStreamSource) {
|
||||
this.mediaStreamSource.disconnect();
|
||||
this.mediaStreamSource = null;
|
||||
}
|
||||
this.audioTrack = stream.getAudioTracks()[0];
|
||||
const mediaStream = new MediaStream();
|
||||
mediaStream.addTrack(this.audioTrack);
|
||||
this.mediaStreamSource = this.audioContext.createMediaStreamSource(mediaStream);
|
||||
if (WebRecorder.isSupportMediaStreamSource(requestId, this.audioContext)) {
|
||||
if (WebRecorder.isSupportAudioWorklet(this.audioContext)) { // 不支持 AudioWorklet 降级
|
||||
this.audioWorkletNodeDealAudioData(this.mediaStreamSource, requestId);
|
||||
} else {
|
||||
this.scriptNodeDealAudioData(this.mediaStreamSource, requestId);
|
||||
}
|
||||
} else { // 不支持 MediaStreamSource
|
||||
this.isLog && console.log(this.requestId, '不支持MediaStreamSource', TAG);
|
||||
this.OnError('不支持MediaStreamSource');
|
||||
}
|
||||
}
|
||||
getAudioFail(requestId, err) {
|
||||
if (err && err.err && err.err.name === 'NotAllowedError') {
|
||||
this.isLog && console.log(requestId,'授权失败', JSON.stringify(err.err), TAG);
|
||||
}
|
||||
this.isLog && console.log(this.requestId, 'getAudioFail', JSON.stringify(err), TAG);
|
||||
this.OnError(err);
|
||||
this.stop();
|
||||
}
|
||||
scriptNodeDealAudioData(mediaStreamSource, requestId) {
|
||||
if (WebRecorder.isSupportCreateScriptProcessor(requestId, this.audioContext)) {
|
||||
// 创建一个音频分析对象,采样的缓冲区大小为0(自动适配),输入和输出都是单声道
|
||||
const scriptProcessor = this.audioContext.createScriptProcessor(1024, 1, 1);
|
||||
// 连接
|
||||
this.mediaStreamSource && this.mediaStreamSource.connect(scriptProcessor);
|
||||
scriptProcessor && scriptProcessor.connect(this.audioContext.destination);
|
||||
scriptProcessor.onaudioprocess = (e) => {
|
||||
this.getDataCount += 1;
|
||||
// 去处理音频数据
|
||||
const inputData = e.inputBuffer.getChannelData(0);
|
||||
const output = to16kHz(inputData, this.audioContext.sampleRate);
|
||||
const audioData = to16BitPCM(output);
|
||||
this.audioData.push(...new Int8Array(audioData.buffer));
|
||||
this.allAudioData.push(...new Int8Array(audioData.buffer));
|
||||
if (new Date().getTime() - this.preTime > 100) {
|
||||
this.frameTime.push(`${Date.now()}-${this.frameCount}`);
|
||||
this.frameCount += 1;
|
||||
this.preTime = new Date().getTime();
|
||||
const audioDataArray = new Int8Array(this.audioData);
|
||||
this.OnReceivedData(audioDataArray);
|
||||
this.audioData = [];
|
||||
this.sampleCount += 1;
|
||||
this.bitCount += 1;
|
||||
}
|
||||
};
|
||||
} else { // 不支持
|
||||
this.isLog && console.log(this.requestId, '不支持createScriptProcessor', TAG);
|
||||
}
|
||||
}
|
||||
async audioWorkletNodeDealAudioData(mediaStreamSource, requestId) {
|
||||
try {
|
||||
const audioWorkletBlobURL = window.URL.createObjectURL(new Blob([audioWorkletCode], { type: 'text/javascript' }));
|
||||
await this.audioContext.audioWorklet.addModule(audioWorkletBlobURL);
|
||||
const myNode = new AudioWorkletNode(this.audioContext, 'my-processor', { numberOfInputs: 1, numberOfOutputs: 1, channelCount: 1 });
|
||||
myNode.onprocessorerror = (event) => {
|
||||
// 降级
|
||||
this.scriptNodeDealAudioData(mediaStreamSource, this.requestId);
|
||||
return false;
|
||||
}
|
||||
myNode.port.onmessage = (event) => {
|
||||
this.frameTime.push(`${Date.now()}-${this.frameCount}`);
|
||||
this.OnReceivedData(event.data.audioData);
|
||||
this.frameCount += 1;
|
||||
this.allAudioData.push(...event.data.audioData);
|
||||
this.sampleCount = event.data.sampleCount;
|
||||
this.bitCount = event.data.bitCount;
|
||||
};
|
||||
myNode.port.onmessageerror = (event) => {
|
||||
// 降级
|
||||
this.scriptNodeDealAudioData(mediaStreamSource, requestId);
|
||||
return false;
|
||||
}
|
||||
mediaStreamSource &&mediaStreamSource.connect(myNode).connect(this.audioContext.destination);
|
||||
} catch (e) {
|
||||
this.isLog && console.log(this.requestId, 'audioWorkletNodeDealAudioData catch error', JSON.stringify(e), TAG);
|
||||
this.OnError(e);
|
||||
}
|
||||
}
|
||||
// 获取音频数据
|
||||
OnReceivedData(data) {}
|
||||
OnError(res) {}
|
||||
OnStop(res) {}
|
||||
}
|
||||
typeof window !== 'undefined' && (window.WebRecorder = WebRecorder);
|
|
@ -0,0 +1,31 @@
|
|||
/** 获取签名 start */
|
||||
|
||||
function toUint8Array(wordArray) {
|
||||
// Shortcuts
|
||||
const words = wordArray.words;
|
||||
const sigBytes = wordArray.sigBytes;
|
||||
|
||||
// Convert
|
||||
const u8 = new Uint8Array(sigBytes);
|
||||
for (let i = 0; i < sigBytes; i++) {
|
||||
u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
}
|
||||
return u8;
|
||||
}
|
||||
|
||||
function Uint8ArrayToString(fileData){
|
||||
let dataString = '';
|
||||
for (let i = 0; i < fileData.length; i++) {
|
||||
dataString += String.fromCharCode(fileData[i]);
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
// 签名函数示例
|
||||
function signCallback(signStr) {
|
||||
const secretKey = config.secretKey;
|
||||
const hash = window.CryptoJSTest.HmacSHA1(signStr, secretKey);
|
||||
const bytes = Uint8ArrayToString(toUint8Array(hash));
|
||||
return window.btoa(bytes);
|
||||
}
|
||||
|
||||
/** 获取签名 end */
|
|
@ -0,0 +1,7 @@
|
|||
let config = {
|
||||
secretKey: 'QcbMitrb7QVMyNaZJp7E7mESSFrGUWKz',
|
||||
secretId: 'AKIDJfpnZ9EF5i7II4cw3xfARVMyNYeljU3Q',
|
||||
appId: 1255881128,
|
||||
}
|
||||
window.config = config
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>测试 demo</title>
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content-box">
|
||||
<div class="content">
|
||||
<div class="content-item">
|
||||
<button class="button" id="start">开始识别</button>
|
||||
<div class="connecting display-none" id="connecting">建立连接中...</div>
|
||||
<span class="recognizing display-none" id="recognizing">识别中...</span>
|
||||
<button class="button end-btn" id="end">结束识别</button>
|
||||
</div>
|
||||
<div class="content-item">
|
||||
<div id="recognizeText" class="recognize-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="lib/jquery.js"></script>
|
||||
<script src="config.js"></script>
|
||||
<script src="asrauthentication.js"></script>
|
||||
<script src="../dist/speechrecognizer.js"></script>
|
||||
<script type="module" src="./main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
.content-box .content{
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
margin: 100px auto;
|
||||
}
|
||||
.content .content-item {
|
||||
width: 50%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
}
|
||||
.left-content {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
.connecting {
|
||||
color: #999;
|
||||
margin: 50px 0;
|
||||
}
|
||||
.recognizing {
|
||||
color: #999;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.content .content-item .button {
|
||||
background: #187cff;
|
||||
border: 1px solid #478eea;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
width: 160px;
|
||||
height: 40px;
|
||||
margin: 50px 0;
|
||||
}
|
||||
.button.end-btn {
|
||||
display: none;
|
||||
}
|
||||
.recognize-content {
|
||||
height: 200px;
|
||||
width: 360px;
|
||||
background-color: #eee;
|
||||
border: 1px solid #eee;
|
||||
border-radius: 5px;
|
||||
margin: auto;
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
overflow: scroll;
|
||||
}
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>测试 demo</title>
|
||||
<link rel="stylesheet" href="index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content-box">
|
||||
<div class="content">
|
||||
<div class="content-item left-content">
|
||||
<button class="button" id="start">开始识别</button>
|
||||
<div class="connecting display-none" id="connecting">建立连接中...</div>
|
||||
<span class="recognizing display-none" id="recognizing">识别中...</span>
|
||||
<button class="button end-btn" id="end">结束识别</button>
|
||||
</div>
|
||||
<div class="content-item">
|
||||
<div id="recognizeText" class="recognize-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="lib/jquery.js"></script>
|
||||
<script src="../dist/speechrecognizer.js"></script>
|
||||
<script src="config.js"></script>
|
||||
<script src="asrauthentication.js"></script>
|
||||
<script type="module" src="index.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,77 @@
|
|||
let webAudioSpeechRecognizer;
|
||||
let isCanStop;
|
||||
$(function () {
|
||||
const params = {
|
||||
signCallback: signCallback, // 鉴权函数,若直接使用默认鉴权函数。可不传此参数
|
||||
// 用户参数
|
||||
secretid: config.secretId,
|
||||
secretkey: config.secretKey,
|
||||
appid: config.appId,
|
||||
// 临时密钥参数,非必填
|
||||
// token: config.token,
|
||||
// 实时识别接口参数
|
||||
engine_model_type : '16k_zh', // 因为内置WebRecorder采样16k的数据,所以参数 engineModelType 需要选择16k的引擎,为 '16k_zh'
|
||||
// 以下为非必填参数,可跟据业务自行修改
|
||||
// voice_format : 1,
|
||||
// hotword_id : '08003a00000000000000000000000000',
|
||||
// needvad: 1,
|
||||
// filter_dirty: 1,
|
||||
// filter_modal: 2,
|
||||
// filter_punc: 0,
|
||||
// convert_num_mode : 1,
|
||||
// word_info: 2
|
||||
}
|
||||
$('#start').on('click', function () {
|
||||
webAudioSpeechRecognizer = new WebAudioSpeechRecognizer(params);
|
||||
const areaDom = $('#recognizeText');
|
||||
areaDom.text('');
|
||||
let resultText = '';
|
||||
$(this).hide();
|
||||
$('#connecting').show();
|
||||
// 开始识别
|
||||
webAudioSpeechRecognizer.OnRecognitionStart = (res) => {
|
||||
console.log('开始识别', res);
|
||||
};
|
||||
// 一句话开始
|
||||
webAudioSpeechRecognizer.OnSentenceBegin = (res) => {
|
||||
console.log('一句话开始', res);
|
||||
isCanStop = true;
|
||||
$('#end').show();
|
||||
$('#recognizing').show();
|
||||
$('#connecting').hide();
|
||||
};
|
||||
// 识别变化时
|
||||
webAudioSpeechRecognizer.OnRecognitionResultChange = (res) => {
|
||||
console.log('识别变化时', res);
|
||||
const currentText = `${resultText}${res.result.voice_text_str}`;
|
||||
areaDom.text(currentText);
|
||||
};
|
||||
// 一句话结束
|
||||
webAudioSpeechRecognizer.OnSentenceEnd = (res) => {
|
||||
console.log('一句话结束', res);
|
||||
resultText += res.result.voice_text_str;
|
||||
areaDom.text(resultText);
|
||||
};
|
||||
// 识别结束
|
||||
webAudioSpeechRecognizer.OnRecognitionComplete = (res) => {
|
||||
console.log('识别结束', res);
|
||||
};
|
||||
// 识别错误
|
||||
webAudioSpeechRecognizer.OnError = (res) => {
|
||||
console.log('识别失败', res)
|
||||
$('#end').hide();
|
||||
$('#recognizing').hide();
|
||||
$('#start').show();
|
||||
$('#connecting').hide();
|
||||
};
|
||||
webAudioSpeechRecognizer.start();
|
||||
});
|
||||
$('#end').on('click', function () {
|
||||
$(this).hide();
|
||||
$('#recognizing').hide();
|
||||
$('#start').show();
|
||||
if (isCanStop) {
|
||||
webAudioSpeechRecognizer.stop();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,272 @@
|
|||
/*
|
||||
* [js-sha1]
|
||||
*
|
||||
* @version 0.6.0
|
||||
* @copyright H, J-C 2018-9-28
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
var CryptoJS = CryptoJS || function (g, l) {
|
||||
var e = {}, d = e.lib = {}, m = function () { }, k = d.Base = {
|
||||
extend: function (a) {
|
||||
m.prototype = this;
|
||||
var c = new m;
|
||||
a && c.mixIn(a);
|
||||
c.hasOwnProperty("init") || (c.init = function () {
|
||||
c.$super.init.apply(this, arguments)
|
||||
});
|
||||
c.init.prototype = c;
|
||||
c.$super = this;
|
||||
return c
|
||||
},
|
||||
create: function () {
|
||||
var a = this.extend();
|
||||
a.init.apply(a, arguments);
|
||||
return a
|
||||
},
|
||||
init: function () { },
|
||||
mixIn: function (a) {
|
||||
for (var c in a) a.hasOwnProperty(c) && (this[c] = a[c]);
|
||||
a.hasOwnProperty("toString") && (this.toString = a.toString)
|
||||
},
|
||||
clone: function () {
|
||||
return this.init.prototype.extend(this)
|
||||
}
|
||||
},
|
||||
p = d.WordArray = k.extend({
|
||||
init: function (a, c) {
|
||||
a = this.words = a || [];
|
||||
this.sigBytes = c != l ? c : 4 * a.length
|
||||
},
|
||||
toString: function (a) {
|
||||
return (a || n).stringify(this)
|
||||
},
|
||||
concat: function (a) {
|
||||
var c = this.words,
|
||||
q = a.words,
|
||||
f = this.sigBytes;
|
||||
a = a.sigBytes;
|
||||
this.clamp();
|
||||
if (f % 4)
|
||||
for (var b = 0; b < a; b++) c[f + b >>> 2] |= (q[b >>> 2] >>> 24 - 8 * (b % 4) & 255) << 24 - 8 * ((f + b) % 4);
|
||||
else if (65535 < q.length)
|
||||
for (b = 0; b < a; b += 4) c[f + b >>> 2] = q[b >>> 2];
|
||||
else c.push.apply(c, q);
|
||||
this.sigBytes += a;
|
||||
return this
|
||||
},
|
||||
clamp: function () {
|
||||
var a = this.words,
|
||||
c = this.sigBytes;
|
||||
a[c >>> 2] &= 4294967295 << 32 - 8 * (c % 4);
|
||||
a.length = g.ceil(c / 4)
|
||||
},
|
||||
clone: function () {
|
||||
var a = k.clone.call(this);
|
||||
a.words = this.words.slice(0);
|
||||
return a
|
||||
},
|
||||
random: function (a) {
|
||||
for (var c = [], b = 0; b < a; b += 4) c.push(4294967296 * g.random() | 0);
|
||||
return new p.init(c, a)
|
||||
}
|
||||
}),
|
||||
b = e.enc = {}, n = b.Hex = {
|
||||
stringify: function (a) {
|
||||
var c = a.words;
|
||||
a = a.sigBytes;
|
||||
for (var b = [], f = 0; f < a; f++) {
|
||||
var d = c[f >>> 2] >>> 24 - 8 * (f % 4) & 255;
|
||||
b.push((d >>> 4).toString(16));
|
||||
b.push((d & 15).toString(16))
|
||||
}
|
||||
return b.join("")
|
||||
},
|
||||
parse: function (a) {
|
||||
for (var c = a.length, b = [], f = 0; f < c; f += 2) b[f >>> 3] |= parseInt(a.substr(f, 2), 16) << 24 - 4 * (f % 8);
|
||||
return new p.init(b, c / 2)
|
||||
}
|
||||
}, j = b.Latin1 = {
|
||||
stringify: function (a) {
|
||||
var c = a.words;
|
||||
a = a.sigBytes;
|
||||
for (var b = [], f = 0; f < a; f++) b.push(String.fromCharCode(c[f >>> 2] >>> 24 - 8 * (f % 4) & 255));
|
||||
return b.join("")
|
||||
},
|
||||
parse: function (a) {
|
||||
for (var c = a.length, b = [], f = 0; f < c; f++) b[f >>> 2] |= (a.charCodeAt(f) & 255) << 24 - 8 * (f % 4);
|
||||
return new p.init(b, c)
|
||||
}
|
||||
}, h = b.Utf8 = {
|
||||
stringify: function (a) {
|
||||
try {
|
||||
return decodeURIComponent(escape(j.stringify(a)))
|
||||
} catch (c) {
|
||||
throw Error("Malformed UTF-8 data");
|
||||
}
|
||||
},
|
||||
parse: function (a) {
|
||||
return j.parse(unescape(encodeURIComponent(a)))
|
||||
}
|
||||
},
|
||||
r = d.BufferedBlockAlgorithm = k.extend({
|
||||
reset: function () {
|
||||
this._data = new p.init;
|
||||
this._nDataBytes = 0
|
||||
},
|
||||
_append: function (a) {
|
||||
"string" == typeof a && (a = h.parse(a));
|
||||
this._data.concat(a);
|
||||
this._nDataBytes += a.sigBytes
|
||||
},
|
||||
_process: function (a) {
|
||||
var c = this._data,
|
||||
b = c.words,
|
||||
f = c.sigBytes,
|
||||
d = this.blockSize,
|
||||
e = f / (4 * d),
|
||||
e = a ? g.ceil(e) : g.max((e | 0) - this._minBufferSize, 0);
|
||||
a = e * d;
|
||||
f = g.min(4 * a, f);
|
||||
if (a) {
|
||||
for (var k = 0; k < a; k += d) this._doProcessBlock(b, k);
|
||||
k = b.splice(0, a);
|
||||
c.sigBytes -= f
|
||||
}
|
||||
return new p.init(k, f)
|
||||
},
|
||||
clone: function () {
|
||||
var a = k.clone.call(this);
|
||||
a._data = this._data.clone();
|
||||
return a
|
||||
},
|
||||
_minBufferSize: 0
|
||||
});
|
||||
d.Hasher = r.extend({
|
||||
cfg: k.extend(),
|
||||
init: function (a) {
|
||||
this.cfg = this.cfg.extend(a);
|
||||
this.reset()
|
||||
},
|
||||
reset: function () {
|
||||
r.reset.call(this);
|
||||
this._doReset()
|
||||
},
|
||||
update: function (a) {
|
||||
this._append(a);
|
||||
this._process();
|
||||
return this
|
||||
},
|
||||
finalize: function (a) {
|
||||
a && this._append(a);
|
||||
return this._doFinalize()
|
||||
},
|
||||
blockSize: 16,
|
||||
_createHelper: function (a) {
|
||||
return function (b, d) {
|
||||
return (new a.init(d)).finalize(b)
|
||||
}
|
||||
},
|
||||
_createHmacHelper: function (a) {
|
||||
return function (b, d) {
|
||||
return (new s.HMAC.init(a, d)).finalize(b)
|
||||
}
|
||||
}
|
||||
});
|
||||
var s = e.algo = {};
|
||||
return e
|
||||
}(Math);
|
||||
|
||||
(function () {
|
||||
var g = CryptoJS,
|
||||
l = g.lib,
|
||||
e = l.WordArray,
|
||||
d = l.Hasher,
|
||||
m = [],
|
||||
l = g.algo.SHA1 = d.extend({
|
||||
_doReset: function () {
|
||||
this._hash = new e.init([1732584193, 4023233417, 2562383102, 271733878, 3285377520])
|
||||
},
|
||||
_doProcessBlock: function (d, e) {
|
||||
for (var b = this._hash.words, n = b[0], j = b[1], h = b[2], g = b[3], l = b[4], a = 0; 80 > a; a++) {
|
||||
if (16 > a) m[a] = d[e + a] | 0;
|
||||
else {
|
||||
var c = m[a - 3] ^ m[a - 8] ^ m[a - 14] ^ m[a - 16];
|
||||
m[a] = c << 1 | c >>> 31
|
||||
}
|
||||
c = (n << 5 | n >>> 27) + l + m[a];
|
||||
c = 20 > a ? c + ((j & h | ~j & g) + 1518500249) : 40 > a ? c + ((j ^ h ^ g) + 1859775393) : 60 > a ? c + ((j & h | j & g | h & g) - 1894007588) : c + ((j ^ h ^ g) - 899497514);
|
||||
l = g;
|
||||
g = h;
|
||||
h = j << 30 | j >>> 2;
|
||||
j = n;
|
||||
n = c
|
||||
}
|
||||
b[0] = b[0] + n | 0;
|
||||
b[1] = b[1] + j | 0;
|
||||
b[2] = b[2] + h | 0;
|
||||
b[3] = b[3] + g | 0;
|
||||
b[4] = b[4] + l | 0
|
||||
},
|
||||
_doFinalize: function () {
|
||||
var d = this._data,
|
||||
e = d.words,
|
||||
b = 8 * this._nDataBytes,
|
||||
g = 8 * d.sigBytes;
|
||||
e[g >>> 5] |= 128 << 24 - g % 32;
|
||||
e[(g + 64 >>> 9 << 4) + 14] = Math.floor(b / 4294967296);
|
||||
e[(g + 64 >>> 9 << 4) + 15] = b;
|
||||
d.sigBytes = 4 * e.length;
|
||||
this._process();
|
||||
return this._hash
|
||||
},
|
||||
clone: function () {
|
||||
var e = d.clone.call(this);
|
||||
e._hash = this._hash.clone();
|
||||
return e
|
||||
}
|
||||
});
|
||||
g.SHA1 = d._createHelper(l);
|
||||
g.HmacSHA1 = d._createHmacHelper(l)
|
||||
})();
|
||||
|
||||
(function () {
|
||||
var g = CryptoJS,
|
||||
l = g.enc.Utf8;
|
||||
g.algo.HMAC = g.lib.Base.extend({
|
||||
init: function (e, d) {
|
||||
e = this._hasher = new e.init;
|
||||
"string" == typeof d && (d = l.parse(d));
|
||||
var g = e.blockSize,
|
||||
k = 4 * g;
|
||||
d.sigBytes > k && (d = e.finalize(d));
|
||||
d.clamp();
|
||||
for (var p = this._oKey = d.clone(), b = this._iKey = d.clone(), n = p.words, j = b.words, h = 0; h < g; h++) n[h] ^= 1549556828, j[h] ^= 909522486;
|
||||
p.sigBytes = b.sigBytes = k;
|
||||
this.reset()
|
||||
},
|
||||
reset: function () {
|
||||
var e = this._hasher;
|
||||
e.reset();
|
||||
e.update(this._iKey)
|
||||
},
|
||||
update: function (e) {
|
||||
this._hasher.update(e);
|
||||
return this
|
||||
},
|
||||
finalize: function (e) {
|
||||
var d = this._hasher;
|
||||
e = d.finalize(e);
|
||||
d.reset();
|
||||
return d.finalize(this._oKey.clone().concat(e))
|
||||
}
|
||||
})
|
||||
})();
|
||||
|
||||
//使用算法
|
||||
// var key = "f7205fffe445421fdssdfsdfdsfs"
|
||||
// var sha1_result = CryptoJS.HmacSHA1("helloword", key)
|
||||
// console.log('-------',sha1_result.toString())
|
||||
|
||||
|
||||
window && (window.CryptoJSTest = CryptoJS);
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,105 @@
|
|||
let recorder;
|
||||
let speechRecognizer;
|
||||
let isCanSendData = false;
|
||||
let isCanStop;
|
||||
|
||||
$(function () {
|
||||
const params = {
|
||||
signCallback: signCallback, // 鉴权函数 用户提供鉴权函数,不传则为null
|
||||
// 用户参数
|
||||
secretid: config.secretId,
|
||||
appid: config.appId,
|
||||
// 实时识别接口参数
|
||||
engine_model_type : '16k_zh', // 引擎
|
||||
voice_format : 1,
|
||||
// 以下为非必填参数,可跟据业务自行修改
|
||||
hotword_id : '08003a00000000000000000000000000',
|
||||
needvad: 1,
|
||||
filter_dirty: 1,
|
||||
filter_modal: 1,
|
||||
filter_punc: 1,
|
||||
convert_num_mode : 1,
|
||||
word_info: 2
|
||||
}
|
||||
$('#start').on('click', function () {
|
||||
const areaDom = $('#recognizeText');
|
||||
let resultText = '';
|
||||
$(this).hide();
|
||||
$('#connecting').show();
|
||||
speechRecognizer = null;
|
||||
isCanSendData = false;
|
||||
// 获取录音数据
|
||||
recorder = new WebRecorder();
|
||||
recorder.OnReceivedData = (res) => {
|
||||
// console.log(res) // res 为采集到浏览器数据
|
||||
if (isCanSendData) {
|
||||
// 发送数据
|
||||
speechRecognizer.write(res);
|
||||
}
|
||||
};
|
||||
// 录音失败时
|
||||
recorder.OnError = (err) => {
|
||||
console.log(err);
|
||||
recorder.stop();
|
||||
};
|
||||
recorder.start();
|
||||
|
||||
if (!speechRecognizer) {
|
||||
speechRecognizer = new SpeechRecognizer(params);
|
||||
}
|
||||
|
||||
// 开始识别
|
||||
speechRecognizer.OnRecognitionStart = (res) => {
|
||||
console.log('开始识别', res);
|
||||
isCanSendData = true;
|
||||
isCanStop = true;
|
||||
$('#connecting').hide();
|
||||
$('#end').show();
|
||||
$('#recognizing').show();
|
||||
};
|
||||
// 一句话开始
|
||||
speechRecognizer.OnSentenceBegin = (res) => {
|
||||
console.log('一句话开始', res);
|
||||
};
|
||||
// 识别变化时
|
||||
speechRecognizer.OnRecognitionResultChange = (res) => {
|
||||
console.log('识别变化时', res);
|
||||
const currentText = `${resultText}${res.result.voice_text_str}`;
|
||||
areaDom.text(currentText);
|
||||
};
|
||||
// 一句话结束
|
||||
speechRecognizer.OnSentenceEnd = (res) => {
|
||||
console.log('一句话结束', res);
|
||||
resultText += res.result.voice_text_str;
|
||||
areaDom.text(resultText);
|
||||
};
|
||||
// 识别结束
|
||||
speechRecognizer.OnRecognitionComplete = (res) => {
|
||||
console.log('识别结束', res);
|
||||
isCanSendData = false;
|
||||
};
|
||||
// 识别错误
|
||||
speechRecognizer.OnError = (res) => {
|
||||
console.log('识别失败', res);
|
||||
isCanSendData = false;
|
||||
|
||||
$('#end').hide();
|
||||
$('#recognizing').hide();
|
||||
$('#connecting').hide();
|
||||
$('#start').show();
|
||||
};
|
||||
|
||||
// 建立连接
|
||||
speechRecognizer.start();
|
||||
|
||||
});
|
||||
$('#end').on('click', function () {
|
||||
$(this).hide();
|
||||
$('#start').show();
|
||||
$('#recognizing').hide();
|
||||
recorder.stop();
|
||||
if (isCanStop) {
|
||||
speechRecognizer.stop();
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,212 @@
|
|||
import { SpeechRecognizer } from '../../app/speechrecognizer';
|
||||
|
||||
export const guid = () => {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = Math.random() * 16 | 0,
|
||||
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
};
|
||||
const audioWorkletCode = `
|
||||
class MyProcessor extends AudioWorkletProcessor {
|
||||
constructor(options) {
|
||||
super(options);
|
||||
this.audioData = [];
|
||||
this.preTime = 0;
|
||||
}
|
||||
|
||||
process(inputs) {
|
||||
// 去处理音频数据
|
||||
// eslint-disable-next-line no-undef
|
||||
if (inputs[0][0]) {
|
||||
const output = ${to16kHz}(inputs[0][0], sampleRate);
|
||||
const audioData = ${to16BitPCM}(output);
|
||||
const data = [...new Int8Array(audioData.buffer)];
|
||||
this.audioData = this.audioData.concat(data);
|
||||
if (new Date().getTime() - this.preTime > 100) {
|
||||
this.port.postMessage({
|
||||
audioData: new Int8Array(this.audioData)
|
||||
});
|
||||
this.preTime = new Date().getTime();
|
||||
this.audioData = [];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
registerProcessor('my-processor', MyProcessor);
|
||||
`;
|
||||
const audioWorkletBlobURL = window.URL.createObjectURL(new Blob([audioWorkletCode], { type: 'text/javascript' }));
|
||||
const needFiltrationParams = ['appId', 'secretKey', 'secretId', 'audioTrack'];
|
||||
class ASR {
|
||||
constructor(options, isLog) {
|
||||
this.audioTrack = options.audioTrack;
|
||||
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||||
this.speechRecognizer = null;
|
||||
this.isCanSendData = false;
|
||||
this.audioData = [];
|
||||
this.secretkey = options.secretKey;
|
||||
this.params = {
|
||||
...options,
|
||||
secretid: options.secretId,
|
||||
appid: options.appId,
|
||||
};
|
||||
this.isLog = isLog;
|
||||
this.OnRecognitionStart = function () {};
|
||||
this.OnSentenceBegin = function () {};
|
||||
this.OnRecognitionResultChange = function () {};
|
||||
this.OnSentenceEnd = function () {};
|
||||
this.OnRecognitionComplete = function () {};
|
||||
this.OnError = function () {};
|
||||
this.OnChange = function () {};
|
||||
}
|
||||
signCallback(signStr) {
|
||||
const secretKey = this.secretkey;
|
||||
const hash = window.CryptoJSTest.HmacSHA1(signStr, secretKey);
|
||||
const bytes = Uint8ArrayToString(toUint8Array(hash));
|
||||
return window.btoa(bytes);
|
||||
}
|
||||
start() {
|
||||
if (!this.speechRecognizer) {
|
||||
const tempQuery = { ...this.params };
|
||||
for (let i = 0, len = needFiltrationParams.length; i < len; i++) {
|
||||
if (tempQuery.hasOwnProperty(needFiltrationParams[i])) {
|
||||
delete tempQuery[needFiltrationParams[i]];
|
||||
}
|
||||
}
|
||||
const params = {
|
||||
// 用户参数
|
||||
signCallback: this.signCallback.bind(this),
|
||||
...tempQuery,
|
||||
};
|
||||
this.speechRecognizer = new SpeechRecognizer(params, guid(), this.isLog);
|
||||
}
|
||||
|
||||
// 开始识别
|
||||
this.speechRecognizer.OnRecognitionStart = (res) => {
|
||||
this.isCanSendData = true;
|
||||
this.OnRecognitionStart(res);
|
||||
};
|
||||
// 一句话开始
|
||||
this.speechRecognizer.OnSentenceBegin = (res) => {
|
||||
this.OnSentenceBegin(res);
|
||||
this.OnChange(res);
|
||||
};
|
||||
// 识别变化时
|
||||
this.speechRecognizer.OnRecognitionResultChange = (res) => {
|
||||
this.OnRecognitionResultChange(res);
|
||||
this.OnChange(res);
|
||||
};
|
||||
// 一句话结束
|
||||
this.speechRecognizer.OnSentenceEnd = (res) => {
|
||||
this.OnSentenceEnd(res);
|
||||
this.OnChange(res);
|
||||
};
|
||||
// 识别结束
|
||||
this.speechRecognizer.OnRecognitionComplete = (res) => {
|
||||
this.OnRecognitionComplete(res);
|
||||
};
|
||||
// 识别错误
|
||||
this.speechRecognizer.OnError = (res) => {
|
||||
this.isCanSendData = false;
|
||||
this.OnError(res);
|
||||
};
|
||||
|
||||
// 建立连接
|
||||
this.speechRecognizer.start();
|
||||
this.getAudioData();
|
||||
}
|
||||
getAudioData() {
|
||||
const mediaStream = new MediaStream();
|
||||
mediaStream.addTrack(this.audioTrack);
|
||||
const mediaStreamSource = this.audioContext.createMediaStreamSource(mediaStream); // 将声音对象输入这个对象
|
||||
if (this.audioContext.audioWorklet) {
|
||||
this.audioContext.audioWorklet.addModule(audioWorkletBlobURL).then(() => {
|
||||
const myNode = new AudioWorkletNode(this.audioContext, 'my-processor', { numberOfInputs: 1, numberOfOutputs: 1, channelCount: 1 });
|
||||
myNode.port.onmessage = (event) => {
|
||||
if (this.isCanSendData) {
|
||||
this.speechRecognizer.write(event.data.audioData);
|
||||
}
|
||||
};
|
||||
mediaStreamSource.connect(myNode).connect(this.audioContext.destination);
|
||||
})
|
||||
.catch(console.error);
|
||||
} else {
|
||||
// 创建一个音频分析对象,采样的缓冲区大小为0(自动适配),输入和输出都是单声道
|
||||
const scriptProcessor = this.audioContext.createScriptProcessor(0, 1, 1);
|
||||
scriptProcessor.onaudioprocess = (e) => {
|
||||
// 去处理音频数据
|
||||
const inputData = e.inputBuffer.getChannelData(0);
|
||||
const output = to16kHz(inputData, this.audioContext.sampleRate);
|
||||
const audioData = to16BitPCM(output);
|
||||
this.audioData.push(...new Int8Array(audioData.buffer));
|
||||
if (new Date().getTime() - this.preTime > 100) {
|
||||
if (this.isCanSendData) {
|
||||
const audioDataArray = new Int8Array(this.audioData);
|
||||
this.speechRecognizer.write(audioDataArray);
|
||||
this.preTime = new Date().getTime();
|
||||
this.audioData = [];
|
||||
}
|
||||
}
|
||||
};
|
||||
// 连接
|
||||
mediaStreamSource.connect(scriptProcessor);
|
||||
scriptProcessor.connect(this.audioContext.destination);
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
this.speechRecognizer.stop();
|
||||
this.audioContext && this.audioContext.suspend();
|
||||
}
|
||||
}
|
||||
window && (window.ASR = ASR);
|
||||
|
||||
function toUint8Array(wordArray) {
|
||||
// Shortcuts
|
||||
|
||||
const { words } = wordArray;
|
||||
const { sigBytes } = wordArray;
|
||||
|
||||
// Convert
|
||||
const u8 = new Uint8Array(sigBytes);
|
||||
for (let i = 0; i < sigBytes; i++) {
|
||||
u8[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
|
||||
}
|
||||
return u8;
|
||||
}
|
||||
|
||||
function Uint8ArrayToString(fileData) {
|
||||
let dataString = '';
|
||||
for (let i = 0; i < fileData.length; i++) {
|
||||
dataString += String.fromCharCode(fileData[i]);
|
||||
}
|
||||
return dataString;
|
||||
}
|
||||
function to16BitPCM(input) {
|
||||
const dataLength = input.length * (16 / 8);
|
||||
const dataBuffer = new ArrayBuffer(dataLength);
|
||||
const dataView = new DataView(dataBuffer);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < input.length; i++, offset += 2) {
|
||||
const s = Math.max(-1, Math.min(1, input[i]));
|
||||
dataView.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7fff, true);
|
||||
}
|
||||
return dataView;
|
||||
}
|
||||
function to16kHz(audioData, sampleRate = 44100) {
|
||||
const data = new Float32Array(audioData);
|
||||
const fitCount = Math.round(data.length * (16000 / sampleRate));
|
||||
const newData = new Float32Array(fitCount);
|
||||
const springFactor = (data.length - 1) / (fitCount - 1);
|
||||
newData[0] = data[0];
|
||||
for (let i = 1; i < fitCount - 1; i++) {
|
||||
const tmp = i * springFactor;
|
||||
const before = Math.floor(tmp).toFixed();
|
||||
const after = Math.ceil(tmp).toFixed();
|
||||
const atPoint = tmp - before;
|
||||
newData[i] = data[before] + (data[after] - data[before]) * atPoint;
|
||||
}
|
||||
newData[fitCount - 1] = data[data.length - 1];
|
||||
return newData;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@
|
|||
</a-col>
|
||||
<a-col :span="12" style="text-align: center" v-if="szkc != '1'">
|
||||
<a-button type="primary" class="wenZiJiaCu" style="font-size: 18px; height: 45px"
|
||||
><RouterLink target="_blank" :to="{ path: '/site/qaAddPageThpjb', query: { type: 4, ktId } }">同行评价表</RouterLink></a-button
|
||||
><RouterLink target="_blank" :to="{ path: '/site/qaAddPageThpjb', query: { type: 6, ktId } }">同行评价表</RouterLink></a-button
|
||||
>
|
||||
</a-col>
|
||||
<a-col :span="24" v-if="szkc == '1'" style="text-align: center">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div id="siteMain">
|
||||
<div id="maxSite">
|
||||
<a-layout style="height: calc(100vh - 30px)">
|
||||
<a-layout >
|
||||
<headerPage/>
|
||||
<div style="margin-top:10px;background:#fff;">
|
||||
<div style="padding: 20px;width: 98%;">
|
||||
|
@ -18,7 +18,12 @@
|
|||
</div>
|
||||
<div style="width:98%;" v-for="(item,index) in dataSource">
|
||||
<a-comment style="padding: 20px;">
|
||||
<template #author>{{item.studentName}}</template>
|
||||
<template #avatar>
|
||||
<a-avatar :src="'/resource/img/kc/moren.png'" style="background-color: #c4bfbf;"/>
|
||||
</template>
|
||||
<template #author>
|
||||
{{item.studentName}}
|
||||
</template>
|
||||
<template #datetime>
|
||||
<span>{{item.createTime}}</span>
|
||||
</template>
|
||||
|
@ -36,6 +41,9 @@
|
|||
</a-comment>
|
||||
<a-divider style="border-color: #7cb305" dashed />
|
||||
</div>
|
||||
<div>
|
||||
<a-pagination v-model="current" :total="total" @change="handlePageChange" :pageSize="pageSize" style="text-align: right;"/>
|
||||
</div>
|
||||
<div style="padding: 40px;background: #fafafa">
|
||||
<a-row>
|
||||
<a-col :span="24">
|
||||
|
@ -88,7 +96,7 @@ dayjs.extend(relativeTime);
|
|||
const current = ref<number>(0);
|
||||
const total = ref<number>(0);
|
||||
const pageNo = ref<number>(0);
|
||||
const pageSize = ref<number>(50);
|
||||
const pageSize = ref<number>(10);
|
||||
const APagination = Pagination;
|
||||
const AComment = Comment;
|
||||
const { currentRoute } = useRouter();
|
||||
|
@ -121,6 +129,7 @@ dayjs.extend(relativeTime);
|
|||
|
||||
//新增翻页
|
||||
function handlePageChange(page: number) {
|
||||
current.value = page;
|
||||
loadData(page);
|
||||
}
|
||||
|
||||
|
@ -131,10 +140,12 @@ dayjs.extend(relativeTime);
|
|||
});
|
||||
}
|
||||
function loadData(arg){
|
||||
defHttp.get({ url: '/zyTlqContent/zyTlqContent/list', params: { mainId:id+"",column:'createTime',order:'desc' } }).then((res) => {
|
||||
defHttp.get({ url: '/zyTlqContent/zyTlqContent/list', params: { mainId:id+"",column:'createTime',order:'desc',pageNo:current.value,pageSize : pageSize.value } }).then((res) => {
|
||||
console.log(`🚀 ~ defHttp.get ~ res:`, res)
|
||||
dataSource.value = res.records;
|
||||
total.value = res.total;
|
||||
pageNo.value = res.pages;
|
||||
current.value = res.current;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue