Compare commits
27 Commits
20151115-z
...
20250922-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d81f72f5f | ||
|
|
c9c34501ce | ||
|
|
1812c0901c | ||
|
|
13281d8a7d | ||
|
|
5fdf8efedb | ||
|
|
1a475c8612 | ||
|
|
01e4c676fc | ||
|
|
86e25f69f9 | ||
|
|
b8daef0983 | ||
|
|
df45c9d896 | ||
|
|
b9caf2c4ad | ||
|
|
0afd733f47 | ||
|
|
3720b5667d | ||
|
|
72472979bd | ||
| 70000e2e10 | |||
|
|
969c9f6797 | ||
|
|
33406f6964 | ||
|
|
e1f2e91648 | ||
|
|
8c023d459f | ||
|
|
47c1d29ef2 | ||
|
|
a3dab45af0 | ||
|
|
e3422d15ee | ||
|
|
3cef730e61 | ||
|
|
483b57f667 | ||
|
|
be411ec72d | ||
|
|
d7e425ce9d | ||
|
|
8b68489b25 |
22103
package-lock.json
generated
12
package.json
@@ -15,6 +15,7 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mermaid-js/parser": "^0.6.3",
|
||||
"axios": "^0.21.4",
|
||||
"core-js": "^3.6.5",
|
||||
"driver.js": "^0.9.8",
|
||||
@@ -23,9 +24,15 @@
|
||||
"element-ui": "^2.15.7",
|
||||
"file-saver": "^2.0.5",
|
||||
"fuse.js": "^6.4.6",
|
||||
"highlight.js": "^11.11.1",
|
||||
"image-conversion": "^2.1.1",
|
||||
"jsencrypt": "^3.2.1",
|
||||
"json-bigint": "^1.0.0",
|
||||
"katex": "^0.16.25",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-highlightjs": "^4.2.0",
|
||||
"markdown-it-mermaid": "^0.2.5",
|
||||
"mermaid": "^8.13.10",
|
||||
"mockjs": "^1.1.0",
|
||||
"moment": "^2.29.1",
|
||||
"nprogress": "^0.2.0",
|
||||
@@ -43,11 +50,11 @@
|
||||
"vue": "^2.6.11",
|
||||
"vue-awesome-swiper": "^3.1.3",
|
||||
"vue-cookies": "^1.7.4",
|
||||
"vue-katex": "^0.5.0",
|
||||
"vue-pdf": "^4.2.0",
|
||||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-router": "^3.5.2",
|
||||
"vuex": "^3.6.2",
|
||||
"wangeditor": "^4.7.15"
|
||||
"vuex": "^3.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.5.0",
|
||||
@@ -61,6 +68,7 @@
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"less": "^4.1.1",
|
||||
"less-loader": "^6.2.0",
|
||||
"null-loader": "^4.0.1",
|
||||
"sass": "^1.32.13",
|
||||
"sass-loader": "^10.1.0",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
|
||||
BIN
public/images/case-logo.png
Normal file
|
After Width: | Height: | Size: 438 KiB |
60
src/App.vue
@@ -1,25 +1,74 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<div id="app" style="width: 100vw">
|
||||
<keep-alive :include="['case']">
|
||||
<router-view />
|
||||
12312
|
||||
</keep-alive>
|
||||
<!-- 添加AI Call组件 -->
|
||||
<AICall
|
||||
:dialogVisible="showAICall"
|
||||
@close="onCloseAICall"
|
||||
@restore="onRestoreAICall"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { mapGetters, mapState } from 'vuex';
|
||||
import AICall from '@/views/portal/case/AICall.vue';
|
||||
|
||||
export default{
|
||||
name: 'App',
|
||||
computed: {
|
||||
...mapGetters(['userInfo'])
|
||||
components: {
|
||||
AICall
|
||||
},
|
||||
mounted() {
|
||||
computed: {
|
||||
...mapGetters(['userInfo']),
|
||||
...mapState('app', ['showAICall', 'showAICallMinimized'])
|
||||
},
|
||||
methods: {
|
||||
onCloseAICall() {
|
||||
// 通过Vuex关闭AI Call组件
|
||||
this.$store.dispatch('app/setShowAICall', false);
|
||||
},
|
||||
|
||||
onRestoreAICall() {
|
||||
// 通过Vuex显示AI Call组件
|
||||
this.$store.dispatch('app/setShowAICall', true);
|
||||
},
|
||||
|
||||
// 检查当前路由是否应该显示AI弹窗
|
||||
checkRouteForAICall() {
|
||||
const currentRoute = this.$route.name;
|
||||
// 只在case或caseDetail路由显示弹窗
|
||||
if (currentRoute === 'case' || currentRoute === 'caseDetail') {
|
||||
// 设置最小化窗口显示状态为true
|
||||
this.$store.dispatch('app/setShowAICallMinimized', true);
|
||||
// 注意:这里不再强制设置showAICall为true,保留用户之前的操作状态
|
||||
} else {
|
||||
// 其他路由关闭弹窗
|
||||
this.$store.dispatch('app/setShowAICall', false);
|
||||
// 设置最小化窗口显示状态为false
|
||||
this.$store.dispatch('app/setShowAICallMinimized', false);
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
//从状态值中取,因为登录处理,所以移动watch中
|
||||
// console.log(this.userInfo);
|
||||
// if(this.userInfo && this.userInfo.name!=''){
|
||||
// this.$watermark.set(this.userInfo.name+this.userInfo.loginName);
|
||||
// }
|
||||
|
||||
// 初始化检查路由
|
||||
this.checkRouteForAICall();
|
||||
},
|
||||
watch: {
|
||||
// 监听路由变化
|
||||
$route(to, from) {
|
||||
this.checkRouteForAICall();
|
||||
}
|
||||
}
|
||||
// watch:{
|
||||
// userInfo(newVal,oldVal){
|
||||
// if(newVal && newVal.name!=''){
|
||||
@@ -39,4 +88,3 @@
|
||||
box-shadow: 0px 1px 5px 1px rgba(92,98,111,.3);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
30
src/api/boe/aiChat.js
Normal file
@@ -0,0 +1,30 @@
|
||||
import ajax from '@/utils/xajax.js'
|
||||
|
||||
/**
|
||||
* AI聊天对话接口
|
||||
* @param {Object} data - 请求参数
|
||||
* @param {string} data.conversationId - 会话ID,如果为空则创建新会话
|
||||
* @param {string} data.query - 用户提问内容
|
||||
* @returns {Promise} - 返回SSE流
|
||||
*/
|
||||
export function aiChat(data) {
|
||||
return ajax.postJson('http://192.168.3.178/xboe/m/boe/case/ai/chat', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询会话消息记录接口
|
||||
* @param {string} conversationId - 会话ID
|
||||
* @returns {Promise} - 返回会话历史记录
|
||||
*/
|
||||
export function getChatMessages(conversationId) {
|
||||
return ajax.get('/xboe/m/boe/case/ai/messages?conversationId=' + conversationId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 案例专家功能入口显示权限判断接口
|
||||
* 判断当前登录用户是否显示"案例专家"功能入口
|
||||
* @returns {Promise} - 返回是否显示功能入口的布尔值
|
||||
*/
|
||||
export function showCaseAiEntrance() {
|
||||
return ajax.get('/xboe/m/boe/case/ai/show-entrance')
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
import ajax from '../ajax';
|
||||
|
||||
/**
|
||||
* 获取欢迎页数据
|
||||
*/
|
||||
export function getWelcomeData() {
|
||||
return ajax.get('/manageApi/stu/project/socialRecruit/enroll/getStudentEnrollInfo')
|
||||
}
|
||||
|
||||
/**
|
||||
* 报名审批
|
||||
*/
|
||||
export function enrollRequest() {
|
||||
return ajax.get('/manageApi/stu/project/socialRecruit/enroll/socialRecruitRegistrationRequest')
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学习列表
|
||||
* @param {Object} params - 参数对象
|
||||
* @param {string} params.type - 类型:''全部, 'course'在线课, 'exam'考试
|
||||
* @param {string} params.status - 状态:''全部, 'completed'已完成, 'incomplete'未完成
|
||||
* @param {number} params.pageIndex - 页码
|
||||
* @param {number} params.pageSize - 每页条数
|
||||
*/
|
||||
export function getStudyList(params) {
|
||||
return ajax.postJson('/boe/new-employee/study/list', params)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取学习进度
|
||||
*/
|
||||
export function getStudyProgress() {
|
||||
return ajax.get('/boe/new-employee/study/progress')
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载成绩单
|
||||
*/
|
||||
export function downloadScorecard() {
|
||||
// 使用request方法支持responseType: 'blob'
|
||||
return ajax.request({
|
||||
url: '/boe/new-employee/study/scorecard/download',
|
||||
method: 'get',
|
||||
responseType: 'blob'
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 获取社招新员工项目信息
|
||||
* GET
|
||||
*/
|
||||
export function getSzxygProjectInfo() {
|
||||
return ajax.get('/manageApi/admin/project/getSzxygProjectInfo')
|
||||
}
|
||||
|
||||
/**
|
||||
* 社招新员工培训及考试列表查询
|
||||
* POST
|
||||
* @param {Object} params
|
||||
* @param {number} params.pageNo - 页码
|
||||
* @param {number} params.pageSize - 每页条数
|
||||
* @param {string|number} params.studentId - 学员ID
|
||||
* @param {string|number} params.projectId - 项目ID
|
||||
*/
|
||||
export function getStuProjectTaskList(data) {
|
||||
return ajax.postJson('', '/manageApi/stu/project/getStuProjectTaskList', data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 学员项目进度明细查询
|
||||
* POST
|
||||
* @param {string|number} params.studentId - 学员ID
|
||||
* @param {string|number} params.projectId - 项目ID
|
||||
*/
|
||||
export function getStuProjectProcess(data) {
|
||||
return ajax.postJson('', `/manageApi/stu/project/getStuProjectTaskProgress`, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 社招新员工培训在线课程成绩单下载
|
||||
* GET
|
||||
* @param {string|number} projectId - 项目ID
|
||||
* @param {string|number} studentId - 学员ID
|
||||
*/
|
||||
export function downloadSocialRecruitReport(projectId, studentId) {
|
||||
return ajax.request({
|
||||
url: `/manageApi/stu/project/socialRecruit/report/download?projectId=${projectId}&studentId=${studentId}`,
|
||||
method: 'get',
|
||||
responseType: 'arraybuffer',
|
||||
})
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 799 KiB |
|
Before Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 527 KiB |
@@ -1,112 +0,0 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="visible" class="custom-info-message" @click="handleClose">
|
||||
<div class="message-content">
|
||||
<div class="icon-wrapper">
|
||||
<span class="icon-text">X</span>
|
||||
</div>
|
||||
<div class="message-text">{{ message }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CustomErrorMessage",
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
message: "",
|
||||
timer: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show(msg, duration = 3000) {
|
||||
this.message = msg;
|
||||
this.visible = true;
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
this.timer = setTimeout(() => {
|
||||
this.hide();
|
||||
}, duration);
|
||||
},
|
||||
hide() {
|
||||
this.visible = false;
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.hide();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-info-message {
|
||||
position: fixed;
|
||||
top: 20%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 3000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: rgba(255, 241, 240, 1);
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
border: 1px solid rgba(255, 204, 199, 1);
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: rgba(230, 31, 31, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.icon-wrapper .icon-text {
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,112 +0,0 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div v-if="visible" class="custom-info-message" @click="handleClose">
|
||||
<div class="message-content">
|
||||
<div class="icon-wrapper">
|
||||
<span class="icon-text">i</span>
|
||||
</div>
|
||||
<div class="message-text">{{ message }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CustomInfoMessage",
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
message: "",
|
||||
timer: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show(msg, duration = 3000) {
|
||||
this.message = msg;
|
||||
this.visible = true;
|
||||
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
this.timer = setTimeout(() => {
|
||||
this.hide();
|
||||
}, duration);
|
||||
},
|
||||
hide() {
|
||||
this.visible = false;
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.hide();
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-info-message {
|
||||
position: fixed;
|
||||
top: 20%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 3000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: rgba(22, 119, 255, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.icon-wrapper .icon-text {
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.message-text {
|
||||
color: rgba(0, 0, 0, 0.88);
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,185 +0,0 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
:visible.sync="innerVisible"
|
||||
:close-on-click-modal="false"
|
||||
class="custom-strategy-dialog"
|
||||
width="880px"
|
||||
@close="handleClose"
|
||||
>
|
||||
<div class="strategy-container">
|
||||
<div class="strategy-content">
|
||||
<div class="strategy-title">转正攻略</div>
|
||||
<div class="strategy-body">
|
||||
<RichTextEditor v-model="modalContent" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="strategy-footer-btns">
|
||||
<el-button
|
||||
class="footer-btn"
|
||||
type="primary"
|
||||
:loading="processing"
|
||||
@click="handleCancelClick"
|
||||
v-if="showBtn"
|
||||
>
|
||||
关闭
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RichTextEditor from "@/components/RichTextEditor.vue";
|
||||
import { getSzxygProjectInfo } from "@/api/new-employee/newEmployee";
|
||||
|
||||
export default {
|
||||
name: "NewEmployeeGuideDialog",
|
||||
components: { RichTextEditor },
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
processing: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showBtn: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
onConfirm: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
onCancel: {
|
||||
type: Function,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modalContent: "",
|
||||
innerVisible: this.visible,
|
||||
hasLoadedOnce: false,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
visible(val) {
|
||||
this.innerVisible = val;
|
||||
if (val) {
|
||||
this.fetchContent();
|
||||
}
|
||||
},
|
||||
innerVisible(val) {
|
||||
if (val !== this.visible) {
|
||||
this.$emit("update:visible", val);
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (this.visible) {
|
||||
this.fetchContent();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchContent() {
|
||||
if (this.hasLoadedOnce) return;
|
||||
try {
|
||||
const res = await getSzxygProjectInfo();
|
||||
this.modalContent =
|
||||
(res && res.data && res.data.learningGuideConfig) || "";
|
||||
this.hasLoadedOnce = true;
|
||||
} catch (e) {
|
||||
// 保底:避免影响弹窗展示
|
||||
this.modalContent = "";
|
||||
}
|
||||
},
|
||||
handleConfirmClick() {
|
||||
if (typeof this.onConfirm === "function") {
|
||||
this.onConfirm();
|
||||
} else {
|
||||
// 默认行为:关闭并派发事件
|
||||
this.innerVisible = false;
|
||||
this.$emit("confirm");
|
||||
}
|
||||
},
|
||||
handleCancelClick() {
|
||||
if (typeof this.onCancel === "function") {
|
||||
this.onCancel();
|
||||
} else {
|
||||
this.innerVisible = false;
|
||||
this.$emit("cancel");
|
||||
}
|
||||
},
|
||||
handleClose() {
|
||||
this.$emit("close");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.custom-strategy-dialog >>> .el-dialog {
|
||||
background: url("../assets/images/bg_yd.png");
|
||||
background-size: cover;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
box-shadow: 0 8px 32px rgba(0, 43, 121, 0.14);
|
||||
}
|
||||
.strategy-container {
|
||||
position: relative;
|
||||
min-height: 360px;
|
||||
padding: 0px 40px 28px 40px;
|
||||
border-radius: 18px;
|
||||
}
|
||||
.strategy-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.strategy-title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #1e7cfa;
|
||||
line-height: 32px;
|
||||
margin-bottom: 18px;
|
||||
text-align: left;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
.strategy-body {
|
||||
color: #444;
|
||||
font-size: 15px;
|
||||
min-height: 200px;
|
||||
line-height: 2;
|
||||
margin-bottom: 16px;
|
||||
text-align: left;
|
||||
}
|
||||
.strategy-footer-btns {
|
||||
display: flex;
|
||||
margin-top: 18px;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
gap: 14px;
|
||||
}
|
||||
.footer-btn {
|
||||
min-width: 115px;
|
||||
font-size: 16px;
|
||||
border-radius: 6px;
|
||||
height: 40px;
|
||||
}
|
||||
.footer-btn.el-button--primary {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(48, 116, 239, 1) 0%,
|
||||
rgba(81, 146, 238, 1) 100%
|
||||
);
|
||||
border-color: #278cff;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
width: 210px;
|
||||
border-radius: 58px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<template>
|
||||
<div class="editor-wrap">
|
||||
<div ref="editor" style="min-height: 200px"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import E from "wangeditor";
|
||||
|
||||
export default {
|
||||
name: "RichTextEditor",
|
||||
props: {
|
||||
value: { type: String, default: "" },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const editor = new E(this.$refs.editor);
|
||||
editor.config.zIndex = 1000;
|
||||
editor.create();
|
||||
editor.txt.html(this.value || "");
|
||||
editor.disable(); // 只读
|
||||
this.editor = editor;
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
if (this.editor && val !== this.editor.txt.html()) {
|
||||
this.editor.txt.html(val || "");
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.editor) {
|
||||
this.editor.destroy();
|
||||
this.editor = null;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.editor-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
.editor-wrap >>> .w-e-toolbar {
|
||||
display: none;
|
||||
}
|
||||
.editor-wrap >>> .w-e-text-container {
|
||||
border: 0px !important;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
</style>
|
||||
@@ -2,67 +2,35 @@
|
||||
<div>
|
||||
<div class="xtop">
|
||||
<div class="xtop-content">
|
||||
<div class="xtop-left">
|
||||
<div class="xtop-logo">
|
||||
<router-link class="routerLink" to="/index"
|
||||
><img
|
||||
src="../../../assets/logo/logo-white.png"
|
||||
style="width: 161px; height: 27px"
|
||||
/></router-link>
|
||||
</div>
|
||||
<div class="xtop-nav" v-if="userInfo.role === 1">
|
||||
<div class="xtop-nav-item">
|
||||
<a :href="`${webBaseUrl}/index`">首页</a>
|
||||
</div>
|
||||
<div class="xtop-nav-item">
|
||||
<a :href="`${webBaseUrl}/course`">课程</a>
|
||||
</div>
|
||||
<div class="xtop-nav-item">
|
||||
<a :href="`${webBaseUrl}/case`">案例</a>
|
||||
</div>
|
||||
<div class="xtop-nav-item">
|
||||
<a :href="`${webBaseUrl}/article`">文章</a>
|
||||
</div>
|
||||
<div class="xtop-nav-item">
|
||||
<a :href="`${webBaseUrl}/qa`">问答</a>
|
||||
</div>
|
||||
<div class="xtop-nav-item">
|
||||
<el-dropdown placement="bottom" @command="handleCommand">
|
||||
<span
|
||||
class="el-dropdown-link"
|
||||
style="color: #fff; font-size: 16px; cursor: pointer"
|
||||
>专区</span
|
||||
>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="one"
|
||||
>BOE系列公开课</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item command="two" divided
|
||||
>Grow180</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item command="three" divided
|
||||
>管理者进阶</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item command="for" divided
|
||||
>U选小课堂</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item command="five" divided
|
||||
>社招新员工</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item command="seven" divided
|
||||
>教师专区</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="xtop-nav-item">
|
||||
<a :href="`${webBaseUrl}/follow`">我的关注</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xtop-left">
|
||||
<div class="xtop-logo">
|
||||
<router-link class="routerLink" to="/index"><img src="../../../assets/logo/logo-white.png" style="width:161px;height:27px;" /></router-link>
|
||||
</div>
|
||||
<div class="xtop-right">
|
||||
<!-- <div style="margin-left:250px;margin-right:50px;"> -->
|
||||
<!-- <el-input placeholder="搜索" style="width: 260px;border-radius: 20px !important;" @keyup.enter.native="searchJump()" clearable maxlength="50" v-model="keyword" class="input-with-select">
|
||||
<div class="xtop-nav" v-if="userInfo.role === 1">
|
||||
<div class="xtop-nav-item"><a :href="`${webBaseUrl}/index`">首页</a></div>
|
||||
<div class="xtop-nav-item"><a :href="`${webBaseUrl}/course`">课程</a></div>
|
||||
<div class="xtop-nav-item"><a :href="`${webBaseUrl}/case`">案例</a></div>
|
||||
<div class="xtop-nav-item"><a :href="`${webBaseUrl}/article`">文章</a></div>
|
||||
<div class="xtop-nav-item"><a :href="`${webBaseUrl}/qa`">问答</a></div>
|
||||
<div class="xtop-nav-item">
|
||||
<el-dropdown placement="bottom" @command="handleCommand">
|
||||
<span class="el-dropdown-link" style="color:#fff;font-size:16px;cursor: pointer;">专区</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command='one'>BOE系列公开课</el-dropdown-item>
|
||||
<el-dropdown-item command='two' divided>Grow180</el-dropdown-item>
|
||||
<el-dropdown-item command='three' divided>管理者进阶</el-dropdown-item>
|
||||
<el-dropdown-item command='for' divided>U选小课堂</el-dropdown-item>
|
||||
<el-dropdown-item command='five' divided>社招新员工</el-dropdown-item>
|
||||
<el-dropdown-item command="seven" divided>教师专区</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div class="xtop-nav-item"><a :href="`${webBaseUrl}/follow`">我的关注</a></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="xtop-right">
|
||||
<!-- <div style="margin-left:250px;margin-right:50px;"> -->
|
||||
<!-- <el-input placeholder="搜索" style="width: 260px;border-radius: 20px !important;" @keyup.enter.native="searchJump()" clearable maxlength="50" v-model="keyword" class="input-with-select">
|
||||
<el-select v-model="findType" style="width: 75px;" slot="prepend" placeholder="请选择">
|
||||
<el-option label="课程" value="1"></el-option>
|
||||
<el-option label="案例" value="2"></el-option>
|
||||
@@ -73,115 +41,41 @@
|
||||
</el-input> -->
|
||||
<!-- </div> -->
|
||||
<div>
|
||||
<el-badge
|
||||
:value="userMsg"
|
||||
:hidden="userMsg == 0"
|
||||
class="message-count"
|
||||
>
|
||||
<el-link
|
||||
type="primary"
|
||||
:href="`${webBaseUrl}/message/center/index`"
|
||||
:underline="false"
|
||||
>
|
||||
<svg-icon
|
||||
style="margin-right: 0; font-size: 22px"
|
||||
icon-class="messfff"
|
||||
></svg-icon>
|
||||
<el-badge :value="userMsg" :hidden="userMsg==0" class="message-count">
|
||||
<el-link type="primary" :href="`${webBaseUrl}/message/center/index`" :underline="false">
|
||||
<svg-icon style="margin-right: 0;font-size:22px;" icon-class="messfff"></svg-icon>
|
||||
</el-link>
|
||||
</el-badge>
|
||||
</div>
|
||||
<div>
|
||||
<el-dropdown class="person-action-index" style="margin-left: 44px">
|
||||
<el-dropdown class="person-action-index" style="margin-left: 44px;">
|
||||
<span class="el-dropdown-link">
|
||||
<span v-if="current == 'qa'" style="color: #333">{{
|
||||
curIdentity == 3
|
||||
? "管理员"
|
||||
: curIdentity == 2
|
||||
? "教师"
|
||||
: "学员"
|
||||
}}</span>
|
||||
<span v-else style="color: #fff">{{
|
||||
curIdentity == 3
|
||||
? "管理员"
|
||||
: curIdentity == 2
|
||||
? "教师"
|
||||
: "学员"
|
||||
}}</span
|
||||
><i class="el-icon-arrow-down el-icon--right"></i>
|
||||
<span v-if="current == 'qa'" style="color: #333;">{{curIdentity==3? '管理员':curIdentity==2? '教师':'学员'}}</span>
|
||||
<span v-else style="color: #fff;">{{curIdentity==3? '管理员':curIdentity==2? '教师':'学员'}}</span><i class="el-icon-arrow-down el-icon--right"></i>
|
||||
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item
|
||||
><router-link to="/index">学员</router-link></el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="identity == 2 || identity == 5"
|
||||
@click.native="setCurIdentity(2)"
|
||||
><router-link to="/need/waitaudit"
|
||||
>教师</router-link
|
||||
></el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
v-if="identity == 3 || identity == 5"
|
||||
@click.native="setCurIdentity(3)"
|
||||
><a :href="managerPath + '/learningpath'"
|
||||
>管理员</a
|
||||
></el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item><router-link to="/index">学员</router-link></el-dropdown-item>
|
||||
<el-dropdown-item v-if="identity == 2 || identity == 5" @click.native="setCurIdentity(2)"><router-link to="/need/waitaudit">教师</router-link></el-dropdown-item>
|
||||
<el-dropdown-item v-if="identity == 3 || identity == 5" @click.native="setCurIdentity(3)"><a :href="managerPath+'/learningpath'">管理员</a></el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<div>
|
||||
<el-dropdown>
|
||||
<div
|
||||
style="
|
||||
margin-left: 50px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
"
|
||||
>
|
||||
<el-avatar
|
||||
v-if="userInfo.avatar !== ''"
|
||||
:src="userInfo.avatar"
|
||||
style="
|
||||
vertical-align: middle;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin-right: 10px;
|
||||
"
|
||||
></el-avatar>
|
||||
<span v-else style="display: inline-block">
|
||||
<img
|
||||
v-if="userInfo.sex === 1"
|
||||
src="../../../../public/images/Avatarman.png"
|
||||
alt=""
|
||||
style="width: 30px; height: 30px; vertical-align: middle"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
src="../../../../public/images/Avatarwoman.png"
|
||||
alt=""
|
||||
style="width: 30px; height: 30px; vertical-align: middle"
|
||||
/>
|
||||
</span>
|
||||
<div style="margin-left: 50px;color: #fff;font-size: 14px;cursor: pointer;">
|
||||
<el-avatar v-if="userInfo.avatar !== ''" :src="userInfo.avatar" style="vertical-align: middle;width:40px;height:40px;margin-right:10px;"></el-avatar>
|
||||
<span v-else style="display: inline-block;">
|
||||
<img v-if="userInfo.sex === 1 " src="../../../../public/images/Avatarman.png" alt="" style="width: 30px;height: 30px;vertical-align: middle;">
|
||||
<img v-else src="../../../../public/images/Avatarwoman.png" alt="" style="width: 30px;height: 30px;vertical-align: middle;">
|
||||
</span>
|
||||
|
||||
{{ userInfo.name }}
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="setCurIdentity(1)"
|
||||
><a
|
||||
:href="`${webBaseUrl}${
|
||||
isTiao ? '/uc/study/task' : '/uc/study/courses'
|
||||
}`"
|
||||
>个人中心</a
|
||||
></el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item
|
||||
><router-link :to="'/home/' + userInfo.aid"
|
||||
>个人主页</router-link
|
||||
></el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
{{userInfo.name}}
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item @click.native="setCurIdentity(1)"><a :href="`${webBaseUrl}${isTiao ? '/uc/study/task' : '/uc/study/courses'}`">个人中心</a></el-dropdown-item>
|
||||
<el-dropdown-item><router-link :to="'/home/'+userInfo.aid">个人主页</router-link></el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
||||
@@ -195,53 +89,20 @@
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div> -->
|
||||
<div class="person-action">
|
||||
<el-button @click="logout()" type="text"
|
||||
><svg-icon
|
||||
style="margin-right: 4px; font-size: 16px"
|
||||
icon-class="white-out"
|
||||
></svg-icon
|
||||
>登出</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="person-action">
|
||||
<el-button @click="logout()" type="text"><svg-icon style="margin-right: 4px;font-size:16px;" icon-class="white-out"></svg-icon>登出</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-dialog
|
||||
title="修改登录密码"
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="pwdDlg.show"
|
||||
width="600px"
|
||||
custom-class="g-dialog"
|
||||
>
|
||||
<el-dialog title="修改登录密码" :close-on-click-modal="false" :visible.sync="pwdDlg.show" width="600px" custom-class="g-dialog">
|
||||
<el-form :model="pwdDlg" size="medium" label-width="100px">
|
||||
<el-form-item label="当前密码"
|
||||
><el-input
|
||||
v-model="pwdDlg.nowPwd"
|
||||
type="password"
|
||||
placeholder="当前的登录密码"
|
||||
:style="{ width: '100%' }"
|
||||
></el-input
|
||||
></el-form-item>
|
||||
<el-form-item label="当前密码"><el-input v-model="pwdDlg.nowPwd" type="password" placeholder="当前的登录密码" :style="{ width: '100%' }"></el-input></el-form-item>
|
||||
<el-form-item label="新密码">
|
||||
<el-input
|
||||
v-model="pwdDlg.newPwd"
|
||||
type="password"
|
||||
placeholder="新的密码"
|
||||
:maxlength="20"
|
||||
clearable
|
||||
:style="{ width: '100%' }"
|
||||
></el-input>
|
||||
<el-input v-model="pwdDlg.newPwd" type="password" placeholder="新的密码" :maxlength="20" clearable :style="{ width: '100%' }"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码确认">
|
||||
<el-input
|
||||
v-model="pwdDlg.rePwd"
|
||||
type="password"
|
||||
placeholder="和上面新密码一致"
|
||||
:maxlength="20"
|
||||
clearable
|
||||
:style="{ width: '100%' }"
|
||||
></el-input>
|
||||
<el-input v-model="pwdDlg.rePwd" type="password" placeholder="和上面新密码一致" :maxlength="20" clearable :style="{ width: '100%' }"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
@@ -249,37 +110,14 @@
|
||||
<el-button type="primary" @click="submitPwd">提交修改</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
custom-class="dialog-signin"
|
||||
:show-close="false"
|
||||
:visible.sync="signInShow"
|
||||
:close-on-click-modal="false"
|
||||
width="520px"
|
||||
top="8vh"
|
||||
>
|
||||
<div>
|
||||
<img
|
||||
usemap="#HotMap"
|
||||
src="/pc/ad/dlg.png"
|
||||
style="width: 416px; height: 576px"
|
||||
/>
|
||||
<map name="HotMap" id="HotMap">
|
||||
<area
|
||||
shape="circle"
|
||||
name="link1"
|
||||
coords="380,40,30"
|
||||
@click="closeSignDlg"
|
||||
href="javascript:;"
|
||||
/>
|
||||
<area
|
||||
shape="rectangle"
|
||||
name="link2"
|
||||
coords="150,400,300,500"
|
||||
target="_blank"
|
||||
href="https://boehrsurvey.wjx.cn/vm/PmreuFN.aspx"
|
||||
/>
|
||||
</map>
|
||||
</div>
|
||||
<el-dialog custom-class="dialog-signin" :show-close="false" :visible.sync="signInShow" :close-on-click-modal="false" width="520px" top="8vh">
|
||||
<div>
|
||||
<img usemap="#HotMap" src="/pc/ad/dlg.png" style="width:416px;height: 576px;">
|
||||
<map name="HotMap" id="HotMap">
|
||||
<area shape="circle" name="link1" coords="380,40,30" @click="closeSignDlg" href="javascript:;" />
|
||||
<area shape="rectangle" name="link2" coords="150,400,300,500" target="_blank" href="https://boehrsurvey.wjx.cn/vm/PmreuFN.aspx" />
|
||||
</map>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<gonggao></gonggao>
|
||||
<div class="u-class">
|
||||
@@ -310,7 +148,9 @@
|
||||
内容涵盖:领导力、市场营销、职场技能、财务知识、法律常识、人力资源、经典国学、人文社科
|
||||
</div>
|
||||
</div>
|
||||
<div class="text_msg">创新力专区课程版权已到期!</div>
|
||||
<div class="text_msg">
|
||||
创新力专区课程版权已到期!
|
||||
</div>
|
||||
<!-- <div class="cyl" @click="cylClick">
|
||||
<div class="tyl_title">
|
||||
<img
|
||||
@@ -335,45 +175,44 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import desk from "@/api/console.js";
|
||||
import { getWelcomeData } from "@/api/new-employee/newEmployee";
|
||||
import Cookies from "vue-cookies";
|
||||
import InterestCollection from "@/components/Portal/interestCollection.vue";
|
||||
import GuideBox from "@/components/Portal/guideBox.vue";
|
||||
import gonggao from "@/components/Gonggao.vue";
|
||||
import { mapGetters } from 'vuex';
|
||||
import desk from '@/api/console.js';
|
||||
import Cookies from 'vue-cookies';
|
||||
import InterestCollection from '@/components/Portal/interestCollection.vue'
|
||||
import GuideBox from '@/components/Portal/guideBox.vue'
|
||||
import gonggao from '@/components/Gonggao.vue';
|
||||
export default {
|
||||
components: { InterestCollection, GuideBox, gonggao },
|
||||
components:{InterestCollection,GuideBox,gonggao},
|
||||
data() {
|
||||
return {
|
||||
uClassShow: false,
|
||||
isTiao: false,
|
||||
managerPath: process.env.VUE_APP_MANAGER_PATH,
|
||||
interestShow: false,
|
||||
checked: true,
|
||||
isTiao:false,
|
||||
managerPath:process.env.VUE_APP_MANAGER_PATH,
|
||||
interestShow:false,
|
||||
checked:true,
|
||||
signInShow: false,
|
||||
signLocalTimesKey: "xboe_sign_dlg_times",
|
||||
keyword: "",
|
||||
findType: "1",
|
||||
signLocalTimesKey:'xboe_sign_dlg_times',
|
||||
keyword: '',
|
||||
findType: '1',
|
||||
mouseIndex: 0,
|
||||
activeIndex: "",
|
||||
activeIndex: '',
|
||||
msg: { num: 0 },
|
||||
user: { name: "" },
|
||||
pwdDlg: { show: false, newPwd: "", nowPwd: "", rePwd: "" },
|
||||
current: 1,
|
||||
user: { name: '' },
|
||||
pwdDlg: { show: false, newPwd: '', nowPwd: '', rePwd: '' },
|
||||
current:1
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["userInfo", "userMsg", "curIdentity", "identity"]),
|
||||
showName: function () {
|
||||
...mapGetters(['userInfo', 'userMsg','curIdentity','identity']),
|
||||
showName: function() {
|
||||
//console.log('name='+this.name);
|
||||
//console.log('loginName='+this.loginName);
|
||||
if (this.userInfo.name != "") {
|
||||
if (this.userInfo.name != '') {
|
||||
return this.userInfo.name;
|
||||
} else {
|
||||
return this.userInfo.userName;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.interestShow = true;
|
||||
@@ -399,7 +238,7 @@ export default {
|
||||
// localStorage.setItem(this.$xpage.constants.newLoginKey,0);
|
||||
// }
|
||||
// }
|
||||
this.$store.dispatch("refrashMsg");
|
||||
this.$store.dispatch('refrashMsg');
|
||||
},
|
||||
methods: {
|
||||
tylClick() {
|
||||
@@ -408,13 +247,13 @@ export default {
|
||||
cylClick() {
|
||||
window.open("https://u.boe.com/pc/course?keyword=创新力专区");
|
||||
},
|
||||
setCurIdentity(iden) {
|
||||
this.$store.dispatch("SetCurIdentity", iden);
|
||||
setCurIdentity(iden){
|
||||
this.$store.dispatch('SetCurIdentity',iden);
|
||||
},
|
||||
closeSignDlg(){
|
||||
this.signInShow=false;
|
||||
},
|
||||
closeSignDlg() {
|
||||
this.signInShow = false;
|
||||
},
|
||||
async handleCommand(val) {
|
||||
handleCommand(val) {
|
||||
// let obj = {
|
||||
// one: process.env.VUE_APP_BOE_WEB_URL+'/web/teacherLesson',
|
||||
// two: process.env.VUE_APP_BOE_WEB_URL+'/grow180/login',
|
||||
@@ -422,16 +261,6 @@ export default {
|
||||
// four: 'https://m.qingxuetang.com/x/?appId=qxtcorp306130',
|
||||
// five: process.env.VUE_APP_BOE_WEB_URL+'/boe/new-employee/index.html'
|
||||
// };
|
||||
if (val === "five") {
|
||||
// 从后端获取用户信息和报名状态
|
||||
const res = await getWelcomeData();
|
||||
if (res.status === 200 && res.data) {
|
||||
if (res.data?.approvalResults && res.data?.approvalResults != 1) {
|
||||
this.$router.push("/new-employee/study");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val === "for") {
|
||||
this.uClassShow = true;
|
||||
} else {
|
||||
@@ -442,14 +271,14 @@ export default {
|
||||
two: urlPre + "/grow180/login",
|
||||
three: this.webBaseUrl + "/study/index?study=1",
|
||||
// four: 'https://m.qingxuetang.com/x/?appId=qxtcorp306130',
|
||||
five: this.webBaseUrl + "/new-employee/welcome",
|
||||
seven: this.webBaseUrl + "/grateful/index",
|
||||
five: urlPre + "/boe/new-employee/index.html",
|
||||
seven: this.webBaseUrl + '/grateful/index'
|
||||
};
|
||||
window.open(obj[val]);
|
||||
}
|
||||
},
|
||||
handleUcCommand(val) {
|
||||
if (val == "logout") {
|
||||
handleUcCommand(val){
|
||||
if(val == 'logout'){
|
||||
this.logout();
|
||||
}
|
||||
},
|
||||
@@ -460,39 +289,36 @@ export default {
|
||||
this.pwdDlg.show = true;
|
||||
},
|
||||
submitPwd() {
|
||||
if (
|
||||
this.pwdDlg.nowPwd == "" ||
|
||||
(this.pwdDlg.newPwd == "") | (this.pwdDlg.rePwd == "")
|
||||
) {
|
||||
if (this.pwdDlg.nowPwd == '' || (this.pwdDlg.newPwd == '') | (this.pwdDlg.rePwd == '')) {
|
||||
return;
|
||||
}
|
||||
let params = {
|
||||
loginName: this.loginName,
|
||||
old: this.pwdDlg.nowPwd,
|
||||
newPassword: this.pwdDlg.newPwd,
|
||||
rePassword: this.pwdDlg.rePwd,
|
||||
rePassword: this.pwdDlg.rePwd
|
||||
};
|
||||
desk.updatePassword(params).then((res) => {
|
||||
desk.updatePassword(params).then(res => {
|
||||
if (res.status == 200) {
|
||||
this.$message({ message: "修改成功,请重新登录", type: "success" });
|
||||
this.$message({ message: '修改成功,请重新登录', type: 'success' });
|
||||
} else {
|
||||
this.$message({ message: "修改失败,请检查输入", type: "error" });
|
||||
this.$message({ message: '修改失败,请检查输入', type: 'error' });
|
||||
}
|
||||
});
|
||||
},
|
||||
logout() {
|
||||
this.$confirm("确定退出系统吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
this.$confirm('确定退出系统吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.$store.dispatch("LogOut").then(() => {
|
||||
this.$store.dispatch('LogOut').then(() => {
|
||||
//location.href = this.webBaseUrl + '/login';
|
||||
location.href = process.env.VUE_APP_LOGIN_URL;
|
||||
location.href =process.env.VUE_APP_LOGIN_URL;
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -609,7 +435,7 @@ export default {
|
||||
border: 0.96px solid rgba(140, 105, 242, 1);
|
||||
box-shadow: 7px 5px 6px 0px rgba(76, 31, 221, 0.3);
|
||||
}
|
||||
.text_msg {
|
||||
.text_msg{
|
||||
margin: 43px 0 0 222px;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
@@ -671,86 +497,89 @@ export default {
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .el-dropdown-menu__item:not(.is-disabled):hover {
|
||||
background-color: #fff !important;
|
||||
color: #0059ff !important;
|
||||
::v-deep .el-dropdown-menu__item:not(.is-disabled):hover{
|
||||
background-color: #fff !important;
|
||||
color: #0059FF !important;
|
||||
}
|
||||
::v-deep.el-dropdown-menu {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
border: none !important;
|
||||
|
||||
}
|
||||
::v-deep .el-avatar {
|
||||
margin-right: 8px;
|
||||
img {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
::v-deep.xtop .xtop-right .person-action[data-v-5bcef51e] .message-count i {
|
||||
::v-deep .el-avatar{
|
||||
margin-right: 8px;
|
||||
img{
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
::v-deep.xtop .xtop-right .person-action[data-v-5bcef51e] .message-count i{
|
||||
color: #fff !important;
|
||||
}
|
||||
.el-button--text {
|
||||
.el-button--text{
|
||||
color: #fff;
|
||||
}
|
||||
img {
|
||||
img{
|
||||
|
||||
}
|
||||
::v-deep .dialog-signin {
|
||||
::v-deep .dialog-signin{
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
.el-dialog__header {
|
||||
box-shadow:none;
|
||||
.el-dialog__header{
|
||||
padding: 0;
|
||||
}
|
||||
.el-dialog__body {
|
||||
.el-dialog__body{
|
||||
padding: 0;
|
||||
}
|
||||
.el-dialog__footer {
|
||||
padding: 0;
|
||||
|
||||
}
|
||||
.signin-bg{
|
||||
position: relative;
|
||||
.el-icon-close{
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 10px;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
.signin-bg {
|
||||
position: relative;
|
||||
.el-icon-close {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 10px;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.signin-box2 {
|
||||
margin-left: 100px;
|
||||
font-size: 12px;
|
||||
.signin-checkbox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #ffffff;
|
||||
margin-top: 10px;
|
||||
.el-checkbox {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.signin-text {
|
||||
font-family: Alibaba PuHuiTi;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
color: #003eb7;
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
||||
.signin-box {
|
||||
}
|
||||
.signin-box2{
|
||||
margin-left: 100px;
|
||||
font-size: 12px;
|
||||
.signin-checkbox{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #FFFFFF;
|
||||
margin-top: 10px;
|
||||
.el-checkbox{
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.signin-text{
|
||||
font-family: Alibaba PuHuiTi;
|
||||
font-style:italic;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
color:#003eb7;
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
||||
.signin-box{
|
||||
margin: 0 70px;
|
||||
p {
|
||||
p{
|
||||
line-height: 28px;
|
||||
}
|
||||
.signin-text {
|
||||
.signin-text{
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
color: #ffffff;
|
||||
.ti-yan {
|
||||
border-bottom: 1px solid #ffffff;
|
||||
color: #FFFFFF;
|
||||
.ti-yan{
|
||||
border-bottom: 1px solid #FFFFFF;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
img {
|
||||
img{
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-left: 6px;
|
||||
@@ -758,38 +587,38 @@ img {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
.signin-a {
|
||||
.signin-a{
|
||||
font-size: 12px;
|
||||
font-family: Alibaba PuHuiTi;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
span {
|
||||
border-bottom: 1px solid #ffffff;
|
||||
color: #FFFFFF;
|
||||
span{
|
||||
border-bottom: 1px solid #FFFFFF;
|
||||
}
|
||||
}
|
||||
.signin-b {
|
||||
.signin-b{
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
color:#FFFFFF;
|
||||
text-align: right;
|
||||
.el-checkbox__inner {
|
||||
.el-checkbox__inner{
|
||||
//background-color: #588AFC;
|
||||
border-color: #ffffff;
|
||||
border-color: #FFFFFF;
|
||||
}
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner {
|
||||
background-color: #588afc;
|
||||
.el-checkbox__input.is-checked .el-checkbox__inner{
|
||||
background-color: #588AFC;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dialog-footer-signin {
|
||||
.dialog-footer-signin{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: space-between
|
||||
}
|
||||
.xtop {
|
||||
height: 72px;
|
||||
line-height: 72px;
|
||||
min-width: 1280px;
|
||||
background-color: #0078fc;
|
||||
background-color: #0078FC;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
@@ -799,15 +628,15 @@ img {
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #e9e9e9;
|
||||
z-index: 9999;
|
||||
.xtop-content {
|
||||
.xtop-content{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0 40px;
|
||||
margin:0 40px;
|
||||
.xtop-left {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
.xtop-logo {
|
||||
.xtop-logo{
|
||||
padding-top: 20px;
|
||||
padding-right: 50px;
|
||||
display: flex;
|
||||
@@ -818,26 +647,27 @@ img {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.xtop-nav {
|
||||
display: flex;
|
||||
// justify-content: flex-start;
|
||||
a {
|
||||
a{
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.xtop-nav-item {
|
||||
line-height: 72px;
|
||||
line-height:72px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1366px) {
|
||||
.xtop-nav-item {
|
||||
@media screen and (max-width: 1366px){
|
||||
.xtop-nav-item{
|
||||
padding: 0px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1366px) {
|
||||
.xtop-nav-item {
|
||||
@media screen and (min-width: 1366px){
|
||||
.xtop-nav-item{
|
||||
padding: 0px 24px;
|
||||
}
|
||||
}
|
||||
@@ -846,21 +676,21 @@ img {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
.person-action {
|
||||
.person-action{
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-left: 44px;
|
||||
::v-deep .message-count {
|
||||
::v-deep .message-count{
|
||||
margin-right: 30px;
|
||||
i {
|
||||
i{
|
||||
font-size: 26px;
|
||||
color: #409eff;
|
||||
color:#409EFF;
|
||||
}
|
||||
}
|
||||
|
||||
.el-button {
|
||||
margin-top: 1px;
|
||||
.el-button{
|
||||
margin-top: 1px;
|
||||
// margin-left: 20px;
|
||||
}
|
||||
}
|
||||
@@ -878,7 +708,7 @@ img {
|
||||
}
|
||||
|
||||
.xtop .arrow-down {
|
||||
content: "";
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid dashed dashed;
|
||||
@@ -959,21 +789,21 @@ img {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
.message-count a {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
margin-top: 0 !important;
|
||||
.message-count a{
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
::v-deep .el-badge {
|
||||
::v-deep .el-badge{
|
||||
margin-top: 0 !important;
|
||||
.el-badge__content {
|
||||
.el-badge__content{
|
||||
top: 20px;
|
||||
}
|
||||
}
|
||||
::v-deep .el-link.el-link--primary:hover {
|
||||
color: #588afc;
|
||||
color:#588AFC;
|
||||
}
|
||||
.el-dropdown-link {
|
||||
.el-dropdown-link{
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
81
src/main.js
@@ -3,6 +3,22 @@ import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
|
||||
import vueKatexEs from "vue-katex";
|
||||
import "katex/dist/katex.min.css"
|
||||
|
||||
|
||||
Vue.use(vueKatexEs,{
|
||||
globalOptions:{
|
||||
delimiters:[
|
||||
{left:"$$",right:"$$",display:true},
|
||||
{left:"$",right:"$",display:false},
|
||||
{left:"\\[",right:"\\]",display:true},
|
||||
{left:"\\(",right:"\\)",display:false}
|
||||
],
|
||||
throwOnError:true
|
||||
}
|
||||
})
|
||||
|
||||
//import './mock/index'
|
||||
|
||||
import xpage from '@/utils/xpage'
|
||||
@@ -20,8 +36,8 @@ import '@/assets/styles/uc.scss' // global css
|
||||
import Pagination from "@/components/Pagination";
|
||||
import Remark from "@/components/Remark";
|
||||
import './security'; //security control
|
||||
import VueAwesomeSwiper from 'vue-awesome-swiper';
|
||||
import 'swiper/dist/css/swiper.css';
|
||||
import VueAwesomeSwiper from 'vue-awesome-swiper';
|
||||
import 'swiper/dist/css/swiper.css';
|
||||
Vue.use(VueAwesomeSwiper)
|
||||
import watermark from './utils/warterMark.js'
|
||||
import Bus from './utils/bus.js'
|
||||
@@ -35,44 +51,44 @@ Vue.config.productionTip = false
|
||||
Vue.prototype.$xpage = xpage;
|
||||
|
||||
|
||||
Vue.prototype.msgSuccess = function (msg) {
|
||||
this.$message({ showClose: true, message: msg, type: "success", offset: 50 });
|
||||
Vue.prototype.msgSuccess = function(msg) {
|
||||
this.$message({ showClose: true, message: msg, type: "success", offset: 50 });
|
||||
}
|
||||
//用来筛选图标是否显示的方法,字符串和数组都支持,如果传入其他值或者不传值图标全显示
|
||||
Vue.prototype.$iconFilter = function (hideArray) {
|
||||
let iconOpinion = {
|
||||
isRemark: false,
|
||||
isShare: false,
|
||||
isCollect: false,
|
||||
isPraise: false,
|
||||
isBrowse: false
|
||||
}
|
||||
if (typeof hideArray == 'string') {
|
||||
iconOpinion[hideArray] = true
|
||||
}
|
||||
if (hideArray instanceof Array) {
|
||||
hideArray.forEach(item => {
|
||||
iconOpinion[item] = true;
|
||||
})
|
||||
}
|
||||
return iconOpinion
|
||||
Vue.prototype.$iconFilter = function(hideArray) {
|
||||
let iconOpinion = {
|
||||
isRemark: false,
|
||||
isShare: false,
|
||||
isCollect: false,
|
||||
isPraise: false,
|
||||
isBrowse: false
|
||||
}
|
||||
if (typeof hideArray == 'string') {
|
||||
iconOpinion[hideArray] = true
|
||||
}
|
||||
if (hideArray instanceof Array) {
|
||||
hideArray.forEach(item => {
|
||||
iconOpinion[item] = true;
|
||||
})
|
||||
}
|
||||
return iconOpinion
|
||||
}
|
||||
|
||||
Vue.prototype.msgError = function (msg) {
|
||||
this.$message({ showClose: true, message: msg, type: "error" });
|
||||
Vue.prototype.msgError = function(msg) {
|
||||
this.$message({ showClose: true, message: msg, type: "error" });
|
||||
}
|
||||
|
||||
Vue.prototype.msgInfo = function (msg) {
|
||||
this.$message.info(msg);
|
||||
Vue.prototype.msgInfo = function(msg) {
|
||||
this.$message.info(msg);
|
||||
}
|
||||
|
||||
//高亮显示
|
||||
Vue.prototype.$keywordActiveShow = function (str, keyword) {
|
||||
Vue.prototype.$keywordActiveShow=function(str,keyword){
|
||||
//color:#588afc暂时用老系统颜色,ui给的颜色是这个值
|
||||
if (!str || str.indexOf(keyword) === -1 || keyword == '') {
|
||||
if(!str||str.indexOf(keyword)===-1||keyword==''){
|
||||
return str
|
||||
}
|
||||
return str.replace(keyword, `<span style="color:#FF0000">${keyword}</span>`)
|
||||
return str.replace(keyword,`<span style="color:#FF0000">${keyword}</span>`)
|
||||
}
|
||||
|
||||
Vue.prototype.webBaseUrl = process.env.VUE_APP_PUBLIC_PATH;
|
||||
@@ -83,10 +99,9 @@ Vue.component('Remark', Remark)
|
||||
|
||||
|
||||
Vue.use(Element, {
|
||||
size: VueCookies.get('size') || 'medium' // set element-ui default size
|
||||
size: VueCookies.get('size') || 'medium' // set element-ui default size
|
||||
})
|
||||
|
||||
// VueCookies.set('token', "eyJ0eXBlIjoidG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJjb21wYW55Q29kZSI6IkMwMDEiLCJ1SWQiOiI5NjUzNDIwMjc0OTc2MDcxNjgiLCJjb21wYW55SWQiOiIxMDQxNjczOTc3Mzc5OTQ2NDk2IiwibG9naW5JZCI6IjE5MzgwNTQ2NDY2OTAxNzcwMjYiLCJpc3MiOiJodHRwOi8vdS5ib2UuY29tIiwiR2l2ZW5OYW1lIjoiYm9ldSIsImV4cCI6MTc1MDkxMDQwOTc0NiwidXNlck5hbWUiOiLmnY7njonlhrAiLCJ1c2VySWQiOiI2QjA0OUZBRi1DMzE0LTdDQ0YtMEQyOC0wRDIzRjRDNDI1MzEifQ==.7f03d1ce991253197f0e733ba554496e8de715595b169b505bed8831ede565a9", 1)
|
||||
// 加入百度统计
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.path) {
|
||||
@@ -98,7 +113,7 @@ router.beforeEach((to, from, next) => {
|
||||
})
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
|
||||
@@ -18,27 +18,27 @@ export const constantRoutes = [{
|
||||
path: '',
|
||||
redirect: '/study'
|
||||
},
|
||||
{
|
||||
path: '/loading',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Loading'], resolve),
|
||||
name: 'loading',
|
||||
meta: { title: '正在进入学习中心', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Login'], resolve),
|
||||
name: 'login',
|
||||
meta: { title: '京东方大学堂登录', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Index'], resolve),
|
||||
name: 'index',
|
||||
meta: { title: '首页', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/loading',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Loading'], resolve),
|
||||
name: 'loading',
|
||||
meta: {title: '正在进入学习中心', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Login'], resolve),
|
||||
name: 'login',
|
||||
meta: {title: '京东方大学堂登录', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Index'], resolve),
|
||||
name: 'index',
|
||||
meta: {title: '首页', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
// {
|
||||
// path: '/grateful/index',
|
||||
// hidden: true,
|
||||
@@ -46,343 +46,329 @@ export const constantRoutes = [{
|
||||
// name: 'gratefulIndex',
|
||||
// meta: { title: '感恩教师首页', icon: 'dashboard', noCache: true, affix: false },
|
||||
// },
|
||||
{
|
||||
path: '/grateful',
|
||||
component: Grateful,
|
||||
redirect: '/grateful/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/GratefulHomePage'], resolve),
|
||||
name: 'GratefulHomePage',
|
||||
meta: { title: '感恩教师首页', icon: 'dashboard', noCache: true, affix: true, hidden: false }
|
||||
},
|
||||
{
|
||||
path: 'teacherEmpowerment',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/TeacherEmpowerment'], resolve),
|
||||
name: 'TeacherEmpowerment',
|
||||
meta: { title: '教师赋能', icon: 'dashboard', noCache: true, affix: true, hidden: true }
|
||||
},
|
||||
{
|
||||
path: 'toolDown',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/ToolDown'], resolve),
|
||||
name: 'ToolDown',
|
||||
meta: { title: '工具下载', icon: 'dashboard', noCache: true, affix: true, hidden: true }
|
||||
},
|
||||
{
|
||||
path: 'gratefulNotice',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/GratefulNotice'], resolve),
|
||||
name: 'GratefulNotice',
|
||||
meta: { title: '通知列表', icon: 'dashboard', noCache: true, affix: true, hidden: true }
|
||||
},
|
||||
{
|
||||
path: 'noticeDetail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/NoticeDetail'], resolve),
|
||||
name: 'NoticeDetail',
|
||||
meta: { title: '通知详情', icon: 'dashboard', noCache: true, affix: true, hidden: true }
|
||||
},
|
||||
{
|
||||
path: 'teacherOpinion',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/TeacherOpinion'], resolve),
|
||||
name: 'TeacherOpinion',
|
||||
meta: { title: '师资大全', icon: 'dashboard', noCache: true, affix: true, hidden: true }
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/forward',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/Forward'], resolve),
|
||||
name: 'forward',
|
||||
meta: { title: '详细信息', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/course',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Index'], resolve),
|
||||
name: 'course',
|
||||
meta: { title: '课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/courseRecommended',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/CourseRecommended'], resolve),
|
||||
name: 'courseRecommended',
|
||||
meta: { title: '推荐课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/courseSeries',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/CourseSeries'], resolve),
|
||||
name: 'courseSeries',
|
||||
meta: { title: '系列课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/course/micro',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Micro'], resolve),
|
||||
name: 'courseDetailMicro',
|
||||
meta: { title: '课程详情', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/course/detail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Detail'], resolve),
|
||||
name: 'courseDetail',
|
||||
meta: { title: '课程详情', keepAlive: true, icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
// 课程预览
|
||||
{
|
||||
path: '/course/microPreview',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/microPreview'], resolve),
|
||||
name: 'microPreview',
|
||||
meta: { title: '课程预览', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/course/rePreview',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/rePreview'], resolve),
|
||||
name: 'courseRePreview',
|
||||
meta: { title: '课程预览', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
//
|
||||
// 外来链接
|
||||
{
|
||||
path: '/course/boeframe',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/boeframe'], resolve),
|
||||
name: 'courseBoeframe',
|
||||
meta: { title: '课程预览', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
//外来链接
|
||||
{
|
||||
path: '/course/recorded',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Recorded'], resolve),
|
||||
name: 'courseDetailRecorded',
|
||||
meta: { title: '课程详情', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
//此页面需要指定课程的参数
|
||||
path: '/course/studyindex',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/study/coursenew'], resolve),
|
||||
name: 'courseStudyIndex',
|
||||
meta: { title: '课程学习', keepAlive: true, icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
//讲师认证
|
||||
{
|
||||
path: '/case',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Index'], resolve),
|
||||
name: 'case',
|
||||
meta: { title: '案例', keepAlive: true, icon: 'dashboard', noCache: false, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/case/detail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Detail'], resolve),
|
||||
name: 'caseDetail',
|
||||
meta: { title: '案例详情', keepAlive: true, icon: 'dashboard', noCache: false, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/case/charts',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Charts'], resolve),
|
||||
name: 'caseCharts',
|
||||
meta: { title: '排行榜', keepAlive: true, icon: 'dashboard', noCache: false, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/case/excellent',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Excellent'], resolve),
|
||||
name: 'caseExcellent',
|
||||
meta: { title: '浏览量榜单', keepAlive: true, icon: 'dashboard', noCache: false, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/article',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/article/Index'], resolve),
|
||||
name: 'article',
|
||||
meta: { title: '文章', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/article/detail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/article/Detail'], resolve),
|
||||
name: 'articleDetail',
|
||||
meta: { title: '文章详情', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/article/add',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/article/Add'], resolve),
|
||||
name: 'articleAdd',
|
||||
meta: { title: '发布文章', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/qa',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/qa/Index'], resolve),
|
||||
name: 'qa',
|
||||
meta: { title: '问答', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/message',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/user/Message'], resolve),
|
||||
name: 'userMsg',
|
||||
meta: { title: '我的消息', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/homepage/Index'], resolve),
|
||||
name: 'homePage',
|
||||
redirect: '/home/:id',
|
||||
meta: { title: '个人主页', icon: 'dashboard', noCache: true, affix: false },
|
||||
children: [{
|
||||
path: ':id',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/homepage/page'], resolve),
|
||||
name: 'page',
|
||||
meta: { title: '个人主页', icon: 'dashboard', noCache: true, affix: true }
|
||||
{
|
||||
path: '/grateful',
|
||||
component: Grateful,
|
||||
redirect: '/grateful/index',
|
||||
children: [
|
||||
{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/GratefulHomePage'], resolve),
|
||||
name: 'GratefulHomePage',
|
||||
meta: {title: '感恩教师首页', icon: 'dashboard', noCache: true, affix: true, hidden: false}
|
||||
},
|
||||
{
|
||||
path: 'teacherEmpowerment',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/TeacherEmpowerment'], resolve),
|
||||
name: 'TeacherEmpowerment',
|
||||
meta: {title: '教师赋能', icon: 'dashboard', noCache: true, affix: true, hidden: true}
|
||||
},
|
||||
{
|
||||
path: 'toolDown',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/ToolDown'], resolve),
|
||||
name: 'ToolDown',
|
||||
meta: {title: '工具下载', icon: 'dashboard', noCache: true, affix: true, hidden: true}
|
||||
},
|
||||
{
|
||||
path: 'gratefulNotice',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/GratefulNotice'], resolve),
|
||||
name: 'GratefulNotice',
|
||||
meta: {title: '通知列表', icon: 'dashboard', noCache: true, affix: true, hidden: true}
|
||||
},
|
||||
{
|
||||
path: 'noticeDetail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/NoticeDetail'], resolve),
|
||||
name: 'NoticeDetail',
|
||||
meta: {title: '通知详情', icon: 'dashboard', noCache: true, affix: true, hidden: true}
|
||||
},
|
||||
{
|
||||
path: 'teacherOpinion',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/grateful/TeacherOpinion'], resolve),
|
||||
name: 'TeacherOpinion',
|
||||
meta: {title: '师资大全', icon: 'dashboard', noCache: true, affix: true, hidden: true}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
path: ':id/leaving',
|
||||
path: '/forward',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/homepage/leavingMessage'], resolve),
|
||||
name: 'leavingMessage',
|
||||
meta: { title: '留言', icon: 'dashboard', noCache: true, affix: true }
|
||||
component: (resolve) => require(['@/views/Forward'], resolve),
|
||||
name: 'forward',
|
||||
meta: {title: '详细信息', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/course',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Index'], resolve),
|
||||
name: 'course',
|
||||
meta: {title: '课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/courseRecommended',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/CourseRecommended'], resolve),
|
||||
name: 'courseRecommended',
|
||||
meta: {title: '推荐课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/courseSeries',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/CourseSeries'], resolve),
|
||||
name: 'courseSeries',
|
||||
meta: {title: '系列课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/course/micro',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Micro'], resolve),
|
||||
name: 'courseDetailMicro',
|
||||
meta: {title: '课程详情', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/course/detail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Detail'], resolve),
|
||||
name: 'courseDetail',
|
||||
meta: {title: '课程详情', keepAlive: true, icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
// 课程预览
|
||||
{
|
||||
path: '/course/microPreview',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/microPreview'], resolve),
|
||||
name: 'microPreview',
|
||||
meta: {title: '课程预览', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/course/rePreview',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/rePreview'], resolve),
|
||||
name: 'courseRePreview',
|
||||
meta: {title: '课程预览', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
//
|
||||
// 外来链接
|
||||
{
|
||||
path: '/course/boeframe',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/boeframe'], resolve),
|
||||
name: 'courseBoeframe',
|
||||
meta: {title: '课程预览', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
//外来链接
|
||||
{
|
||||
path: '/course/recorded',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/Recorded'], resolve),
|
||||
name: 'courseDetailRecorded',
|
||||
meta: {title: '课程详情', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
//此页面需要指定课程的参数
|
||||
path: '/course/studyindex',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/study/coursenew'], resolve),
|
||||
name: 'courseStudyIndex',
|
||||
meta: {title: '课程学习', keepAlive: true, icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
//讲师认证
|
||||
{
|
||||
path: '/case',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Index'], resolve),
|
||||
name: 'case',
|
||||
meta: {title: '案例', keepAlive: true, icon: 'dashboard', noCache: false, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/case/detail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Detail'], resolve),
|
||||
name: 'caseDetail',
|
||||
meta: {title: '案例详情', keepAlive: true, icon: 'dashboard', noCache: false, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/case/charts',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Charts'], resolve),
|
||||
name: 'caseCharts',
|
||||
meta: {title: '排行榜', keepAlive: true, icon: 'dashboard', noCache: false, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/case/excellent',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/case/Excellent'], resolve),
|
||||
name: 'caseExcellent',
|
||||
meta: {title: '浏览量榜单', keepAlive: true, icon: 'dashboard', noCache: false, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/article',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/article/Index'], resolve),
|
||||
name: 'article',
|
||||
meta: {title: '文章', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/article/detail',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/article/Detail'], resolve),
|
||||
name: 'articleDetail',
|
||||
meta: {title: '文章详情', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/article/add',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/article/Add'], resolve),
|
||||
name: 'articleAdd',
|
||||
meta: {title: '发布文章', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/qa',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/qa/Index'], resolve),
|
||||
name: 'qa',
|
||||
meta: {title: '问答', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/message',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/user/Message'], resolve),
|
||||
name: 'userMsg',
|
||||
meta: {title: '我的消息', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/homepage/Index'], resolve),
|
||||
name: 'homePage',
|
||||
redirect: '/home/:id',
|
||||
meta: {title: '个人主页', icon: 'dashboard', noCache: true, affix: false},
|
||||
children: [{
|
||||
path: ':id',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/homepage/page'], resolve),
|
||||
name: 'page',
|
||||
meta: {title: '个人主页', icon: 'dashboard', noCache: true, affix: true}
|
||||
},
|
||||
{
|
||||
path: ':id/leaving',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/homepage/leavingMessage'], resolve),
|
||||
name: 'leavingMessage',
|
||||
meta: {title: '留言', icon: 'dashboard', noCache: true, affix: true}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/comments',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/Comments'], resolve),
|
||||
name: 'commentReply',
|
||||
meta: {title: '评论回复', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/qa/answer',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/qa/Answer'], resolve),
|
||||
name: 'qaAnswer',
|
||||
meta: {title: '写回答', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/follow',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/follow'], resolve),
|
||||
name: 'follow',
|
||||
meta: {title: '我的关注', icon: 'dashboard', noCache: true, affix: false},
|
||||
},
|
||||
{
|
||||
path: '/study',
|
||||
component: Layout,
|
||||
redirect: '/study/index',
|
||||
children: [{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/StudyIndex'], resolve),
|
||||
name: 'studyIndex',
|
||||
meta: {title: '首页', icon: 'dashboard', noCache: true, affix: true}
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/teacher',
|
||||
component: Layout,
|
||||
children: [{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/TeacherIndex'], resolve),
|
||||
name: 'teacherIndex',
|
||||
meta: {title: '教师个人中心', icon: 'dashboard', noCache: true, affix: true}
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/manager',
|
||||
component: Layout,
|
||||
children: [{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/ManageIndex'], resolve),
|
||||
name: 'managerIndex',
|
||||
meta: {title: '管理员个人中心', icon: 'dashboard', noCache: true, affix: true}
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/exam/test',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/exam/Test'], resolve),
|
||||
name: 'test',
|
||||
meta: {title: '京东方大学堂考试', icon: 'dashboard', noCache: true, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/video/test',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/video'], resolve),
|
||||
name: 'videotest',
|
||||
meta: {title: '课程视频测试', icon: 'dashboard', noCache: true, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/hotforum',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/hotforum/Index'], resolve),
|
||||
name: 'hotforum',
|
||||
meta: {title: '热点论坛', icon: 'dashboard', noCache: true, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/lexiang',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/sso/lexiang'], resolve),
|
||||
name: 'lexiang',
|
||||
meta: {title: '单点登录', icon: 'dashboard', noCache: true, affix: true},
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: (resolve) => require(['@/views/error/404'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/401',
|
||||
component: (resolve) => require(['@/views/error/401'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
component: (resolve) => require(['@/views/error/500'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/qualityCourse',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/qualityCourse'], resolve),
|
||||
name: 'qualityCourse',
|
||||
meta: {title: '精品课课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false},
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/comments',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/Comments'], resolve),
|
||||
name: 'commentReply',
|
||||
meta: { title: '评论回复', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/qa/answer',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/qa/Answer'], resolve),
|
||||
name: 'qaAnswer',
|
||||
meta: { title: '写回答', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/follow',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/follow'], resolve),
|
||||
name: 'follow',
|
||||
meta: { title: '我的关注', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/study',
|
||||
component: Layout,
|
||||
redirect: '/study/index',
|
||||
children: [{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/StudyIndex'], resolve),
|
||||
name: 'studyIndex',
|
||||
meta: { title: '首页', icon: 'dashboard', noCache: true, affix: true }
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/teacher',
|
||||
component: Layout,
|
||||
children: [{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/TeacherIndex'], resolve),
|
||||
name: 'teacherIndex',
|
||||
meta: { title: '教师个人中心', icon: 'dashboard', noCache: true, affix: true }
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/manager',
|
||||
component: Layout,
|
||||
children: [{
|
||||
path: 'index',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/ManageIndex'], resolve),
|
||||
name: 'managerIndex',
|
||||
meta: { title: '管理员个人中心', icon: 'dashboard', noCache: true, affix: true }
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/exam/test',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/exam/Test'], resolve),
|
||||
name: 'test',
|
||||
meta: { title: '京东方大学堂考试', icon: 'dashboard', noCache: true, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/video/test',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/video'], resolve),
|
||||
name: 'videotest',
|
||||
meta: { title: '课程视频测试', icon: 'dashboard', noCache: true, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/hotforum',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/hotforum/Index'], resolve),
|
||||
name: 'hotforum',
|
||||
meta: { title: '热点论坛', icon: 'dashboard', noCache: true, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/lexiang',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/sso/lexiang'], resolve),
|
||||
name: 'lexiang',
|
||||
meta: { title: '单点登录', icon: 'dashboard', noCache: true, affix: true },
|
||||
},
|
||||
{
|
||||
path: '/404',
|
||||
component: (resolve) => require(['@/views/error/404'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/401',
|
||||
component: (resolve) => require(['@/views/error/401'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/500',
|
||||
component: (resolve) => require(['@/views/error/500'], resolve),
|
||||
hidden: true
|
||||
},
|
||||
{
|
||||
path: '/qualityCourse',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/portal/course/qualityCourse'], resolve),
|
||||
name: 'qualityCourse',
|
||||
meta: { title: '精品课课程', keepAlive: true, icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/new-employee/welcome',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/new-employee/WelcomePage'], resolve),
|
||||
name: 'NewEmployeeWelcome',
|
||||
meta: { title: '社招新员工欢迎页', icon: 'dashboard', noCache: true, affix: false },
|
||||
},
|
||||
{
|
||||
path: '/new-employee/study',
|
||||
hidden: true,
|
||||
component: (resolve) => require(['@/views/new-employee/StudyPage'], resolve),
|
||||
name: 'NewEmployeeStudy',
|
||||
meta: { title: '社招新员工学习页面', icon: 'dashboard', noCache: true, affix: false },
|
||||
}
|
||||
|
||||
]
|
||||
|
||||
@@ -394,7 +380,7 @@ const router = new VueRouter({
|
||||
if (saveTop) {
|
||||
return saveTop;
|
||||
} else {
|
||||
return { x: 0, y: 0 }
|
||||
return {x: 0, y: 0}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -7,7 +7,11 @@ const state = {
|
||||
withoutAnimation: false
|
||||
},
|
||||
device: 'desktop',//默认是桌面,以后会有android,ios,minapp
|
||||
size: Cookies.get('size') || 'medium' //字段大小
|
||||
size: Cookies.get('size') || 'medium', //字段大小
|
||||
// 添加AI Call组件显示控制状态
|
||||
showAICall: false,
|
||||
// 控制AI Call最小化窗口在特定路由下显示的状态
|
||||
showAICallMinimized: false
|
||||
}
|
||||
|
||||
const mutations = {
|
||||
@@ -34,6 +38,14 @@ const mutations = {
|
||||
SET_SIZE: (state, size) => {
|
||||
state.size = size
|
||||
Cookies.set('size', size)
|
||||
},
|
||||
// 添加控制AI Call组件显示的mutation
|
||||
SET_SHOW_AI_CALL: (state, show) => {
|
||||
state.showAICall = show
|
||||
},
|
||||
// 控制AI Call最小化窗口显示的mutation
|
||||
SET_SHOW_AI_CALL_MINIMIZED: (state, show) => {
|
||||
state.showAICallMinimized = show
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +61,14 @@ const actions = {
|
||||
},
|
||||
setSize({ commit }, size) {
|
||||
commit('SET_SIZE', size)
|
||||
},
|
||||
// 添加控制AI Call组件显示的action
|
||||
setShowAICall({ commit }, show) {
|
||||
commit('SET_SHOW_AI_CALL', show)
|
||||
},
|
||||
// 控制AI Call最小化窗口显示的action
|
||||
setShowAICallMinimized({ commit }, show) {
|
||||
commit('SET_SHOW_AI_CALL_MINIMIZED', show)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,4 +77,4 @@ export default {
|
||||
state,
|
||||
mutations,
|
||||
actions
|
||||
}
|
||||
}
|
||||
83
src/utils/sseHelper.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* SSE流式数据处理工具
|
||||
*/
|
||||
|
||||
/**
|
||||
* 处理SSE响应数据
|
||||
* @param {String} data - SSE响应数据
|
||||
* @param {Function} onMessage - 处理消息的回调函数
|
||||
* @param {Function} onComplete - 完成时的回调函数
|
||||
* @param {Function} onError - 错误处理回调函数
|
||||
*/
|
||||
export function processSSEData(data, onMessage, onComplete, onError) {
|
||||
try {
|
||||
const lines = data.split('\n')
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const jsonData = JSON.parse(line.substring(6))
|
||||
onMessage(jsonData)
|
||||
|
||||
// 如果状态为4,表示对话结束
|
||||
if (jsonData.data && jsonData.data.status === 4) {
|
||||
onComplete()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('处理SSE数据时出错:', error)
|
||||
if (onError) {
|
||||
onError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SSE连接
|
||||
* @param {String} url - 请求地址
|
||||
* @param {Object} data - 请求数据
|
||||
* @param {Function} onMessage - 消息处理回调
|
||||
* @param {Function} onComplete - 完成回调
|
||||
* @param {Function} onError - 错误回调
|
||||
* @returns {Promise} - 返回fetch Promise
|
||||
*/
|
||||
export function createSSEConnection(url, data, onMessage, onComplete, onError) {
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
}).then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`)
|
||||
}
|
||||
|
||||
const reader = response.body.getReader()
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
let buffer = ''
|
||||
|
||||
function read() {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
if (onComplete) onComplete()
|
||||
return
|
||||
}
|
||||
|
||||
buffer += decoder.decode(value, { stream: true })
|
||||
processSSEData(buffer, onMessage, onComplete, onError)
|
||||
|
||||
// 继续读取
|
||||
read()
|
||||
}).catch(error => {
|
||||
console.error('SSE读取错误:', error)
|
||||
if (onError) onError(error)
|
||||
})
|
||||
}
|
||||
|
||||
// 开始读取数据
|
||||
read()
|
||||
}).catch(error => {
|
||||
console.error('SSE连接错误:', error)
|
||||
if (onError) onError(error)
|
||||
})
|
||||
}
|
||||
@@ -1,872 +0,0 @@
|
||||
<template>
|
||||
<div class="ne-study-page">
|
||||
<div class="banner-header">
|
||||
<portalHeader textColor="#fff" />
|
||||
</div>
|
||||
<!-- 顶部横幅与标题 -->
|
||||
<div class="banner">
|
||||
<div class="banner-inner">
|
||||
<div class="title">社招新员工培训及考试</div>
|
||||
<!-- 筛选条 -->
|
||||
<div class="filters">
|
||||
<div class="filter-group">
|
||||
<span class="label">类型:</span>
|
||||
<el-radio-group
|
||||
v-model="flag"
|
||||
@change="onTypeChange"
|
||||
class="pill-radios"
|
||||
>
|
||||
<el-radio-button label="all">全部</el-radio-button>
|
||||
<el-radio-button label="1">必修</el-radio-button>
|
||||
<el-radio-button label="0">选修</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="filter-group">
|
||||
<span class="label">状态:</span>
|
||||
<el-radio-group
|
||||
v-model="status"
|
||||
@change="onStatusChange"
|
||||
class="pill-radios"
|
||||
>
|
||||
<el-radio-button label="all">全部</el-radio-button>
|
||||
<el-radio-button label="0">未开始</el-radio-button>
|
||||
<el-radio-button label="2">进行中</el-radio-button>
|
||||
<el-radio-button label="1">已完成</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<!-- 左侧主区域 -->
|
||||
<div class="main">
|
||||
<!-- 列表:课程 -->
|
||||
<div class="list" v-loading="loading">
|
||||
<div
|
||||
class="list-item card"
|
||||
v-for="it in items"
|
||||
:key="it.id"
|
||||
@click="toLearn(it.courseId)"
|
||||
>
|
||||
<div style="margin-right: 16px">
|
||||
<img
|
||||
width="160px"
|
||||
src="../../assets/images/course_icon.png"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="item-left">
|
||||
<div>
|
||||
<div class="title-box">
|
||||
<div class="item-title">{{ it.name }}</div>
|
||||
<!-- 右侧动作/成绩 -->
|
||||
<div class="item-right">
|
||||
<el-button
|
||||
v-if="it.status == 0"
|
||||
type="text"
|
||||
style="color: #999999"
|
||||
>未开始</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="it.status == 2"
|
||||
type="text"
|
||||
style="color: #ffb01e"
|
||||
>进行中</el-button
|
||||
>
|
||||
<el-button
|
||||
v-if="it.status == 1"
|
||||
type="text"
|
||||
style="color: #27ce9a"
|
||||
>已完成</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tags">
|
||||
<span class="tag" :class="'tag-blue'"> 在线课 </span>
|
||||
<span v-if="it.flag" class="tag tag-green">必修</span>
|
||||
<span v-else class="tag tag-yellow">选修</span>
|
||||
</div>
|
||||
<div class="progress-wrap">
|
||||
<div class="progress">
|
||||
<div
|
||||
class="bar"
|
||||
:style="{ width: (it.currentRatio || 0) + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="percent">{{ it.currentRatio || 0 }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="total"
|
||||
:page-size="pageSize"
|
||||
:current-page.sync="page"
|
||||
@current-change="onPageChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧侧栏 -->
|
||||
<div class="sidebar">
|
||||
<div class="profile card">
|
||||
<div
|
||||
class="avatar"
|
||||
:style="`background: url(${userInfo.avatar}) no-repeat center/100% 100%;`"
|
||||
></div>
|
||||
<div class="name">{{ userInfo.name }}</div>
|
||||
<div class="desc">{{ userInfo.departName }}</div>
|
||||
<div class="btns">
|
||||
<el-button class="btn btn-outline" @click="onGuide"
|
||||
>转正攻略</el-button
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<div v-if="approvalResults == 2">
|
||||
<el-alert type="warning" center show-icon :closable="false">
|
||||
<template slot="title">
|
||||
<span style="color: rgba(0, 0, 0, 0.88)"
|
||||
>报名申请正在审核中</span
|
||||
>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<div v-if="approvalResults == 3">
|
||||
<el-alert type="success" center show-icon :closable="false">
|
||||
<template slot="title">
|
||||
<span style="color: rgba(0, 0, 0, 0.88)"
|
||||
>报名申请审核已通过</span
|
||||
>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
<div
|
||||
v-if="approvalResults == 4"
|
||||
style="cursor: pointer"
|
||||
@click="returnEnroll"
|
||||
>
|
||||
<el-alert type="error" center show-icon :closable="false">
|
||||
<template slot="title">
|
||||
<span style="color: rgba(0, 0, 0, 0.88)">审核已驳回,请</span>
|
||||
<span style="color: #0078fc">重新报名</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align: center">
|
||||
<el-button type="text" @click="chartVisible = true"
|
||||
>转正流程示意图 ></el-button
|
||||
>
|
||||
</div>
|
||||
<div class="label">成绩单</div>
|
||||
<div class="transcript">
|
||||
<el-button
|
||||
class="btn btn-long"
|
||||
:class="{ 'btn-disabled': isDownloadDisabled }"
|
||||
type="success"
|
||||
icon="el-icon-download"
|
||||
:disabled="isDownloadDisabled"
|
||||
@click="onDownload"
|
||||
>成绩下载</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="label">学习进度</div>
|
||||
<div class="progress-circles">
|
||||
<div class="circle">
|
||||
<div
|
||||
class="ring"
|
||||
:style="`background: conic-gradient(#0078FC 0 ${progressRing.total}%, #eef2f6 ${progressRing.total}%);`"
|
||||
>
|
||||
<span>{{ progressRing.total }}</span>
|
||||
</div>
|
||||
<div class="text">总进度</div>
|
||||
</div>
|
||||
<div class="circle">
|
||||
<div
|
||||
class="ring"
|
||||
:style="`background: conic-gradient(#27ce9a 0 ${progressRing.required}%, #eef2f6 ${progressRing.required}%);`"
|
||||
>
|
||||
<span>{{ progressRing.required }}</span>
|
||||
</div>
|
||||
<div class="text">必修课进度</div>
|
||||
</div>
|
||||
<div class="circle">
|
||||
<div
|
||||
class="ring"
|
||||
:style="`background: conic-gradient(#FFB01E 0 ${progressRing.elective}%, #eef2f6 ${progressRing.elective}%);`"
|
||||
>
|
||||
<span>{{ progressRing.elective }}</span>
|
||||
</div>
|
||||
<div class="text">选修课进度</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<portalFooter />
|
||||
<portalFloatTools />
|
||||
<!-- 学习引导弹窗 -->
|
||||
<NewEmployeeGuideDialog
|
||||
:visible.sync="guideDialogVisible"
|
||||
@close="guideDialogVisible = false"
|
||||
:showBtn="true"
|
||||
/>
|
||||
<!-- 转正流程图弹窗 -->
|
||||
<el-dialog
|
||||
:visible.sync="chartVisible"
|
||||
title="转正流程示意图"
|
||||
:close-on-click-modal="false"
|
||||
@close="chartVisible = false"
|
||||
>
|
||||
<div>
|
||||
<!-- <el-image src="../../assets/images/flowchart.png" fit="fill" /> -->
|
||||
<img width="100%" src="../../assets/images/flowchart.png" alt="" />
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 自定义信息提示 -->
|
||||
<CustomInfoMessage ref="customMessage" />
|
||||
<CustomErrorMessage ref="errorMessage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import portalHeader from "@/components/PortalHeader.vue";
|
||||
import portalFooter from "@/components/PortalFooter.vue";
|
||||
import portalFloatTools from "@/components/PortalFloatTools.vue";
|
||||
import NewEmployeeGuideDialog from "@/components/NewEmployeeGuideDialog.vue";
|
||||
import CustomInfoMessage from "@/components/CustomInfoMessage.vue";
|
||||
import { mapGetters } from "vuex";
|
||||
import {
|
||||
getWelcomeData,
|
||||
getStuProjectTaskList,
|
||||
getStuProjectProcess,
|
||||
downloadSocialRecruitReport,
|
||||
getSzxygProjectInfo,
|
||||
enrollRequest,
|
||||
} from "@/api/new-employee/newEmployee";
|
||||
import CustomErrorMessage from "@/components/CustomErrorMessage.vue";
|
||||
import { start } from "nprogress";
|
||||
|
||||
export default {
|
||||
name: "StudyPage",
|
||||
components: {
|
||||
portalHeader,
|
||||
portalFooter,
|
||||
portalFloatTools,
|
||||
NewEmployeeGuideDialog,
|
||||
CustomInfoMessage,
|
||||
CustomErrorMessage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
flag: "all",
|
||||
status: "all",
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
total: 0,
|
||||
loading: false,
|
||||
projectId: "",
|
||||
studentId: "",
|
||||
// 列表数据(课程/考试混合)
|
||||
items: [],
|
||||
// 进度环数据
|
||||
progressRing: {
|
||||
total: 0,
|
||||
required: 0,
|
||||
elective: 0,
|
||||
},
|
||||
// 学习引导弹窗
|
||||
guideDialogVisible: false,
|
||||
// 转正流程图弹窗
|
||||
chartVisible: false,
|
||||
approvalList: {
|
||||
1: "报名失败",
|
||||
2: "审核中",
|
||||
3: "审核通过",
|
||||
4: "审核失败",
|
||||
},
|
||||
approvalResults: 1,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(["userInfo"]),
|
||||
// 判断下载按钮是否应该禁用
|
||||
isDownloadDisabled() {
|
||||
// 只有在审核通过(3)且学习进度为100时才可下载
|
||||
// 其他情况(报名失败1、审核中2、审核失败4、进度未到100)都禁用
|
||||
return this.approvalResults != 3 || this.progressRing.total != 100;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async initIds() {
|
||||
// studentId 来自用户信息
|
||||
this.studentId = this.userInfo?.aid || this.userInfo?.id || "";
|
||||
const res = await getSzxygProjectInfo();
|
||||
this.projectId = res.data.id || "";
|
||||
},
|
||||
async loadList() {
|
||||
if (!this.studentId || !this.projectId) return;
|
||||
this.loading = true;
|
||||
try {
|
||||
const res = await getStuProjectTaskList({
|
||||
pageNo: this.page,
|
||||
pageSize: this.pageSize,
|
||||
studentId: this.studentId,
|
||||
projectId: this.projectId,
|
||||
status: this.status === "all" ? undefined : this.status,
|
||||
flag: this.flag === "all" ? undefined : this.flag,
|
||||
});
|
||||
const payload = res?.data || {};
|
||||
const records = payload.rows || [];
|
||||
this.total = Number(payload.total || 0);
|
||||
this.items = records;
|
||||
} catch (e) {
|
||||
this.$message.error("加载学习列表失败");
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
},
|
||||
async loadProcess() {
|
||||
if (!this.studentId || !this.projectId) return;
|
||||
try {
|
||||
const res = await getStuProjectProcess({
|
||||
studentId: this.studentId,
|
||||
projectId: this.projectId,
|
||||
});
|
||||
const data = res?.data || {};
|
||||
const total = Number(data.totalProgress ?? 0) | 0;
|
||||
const required = Number(data.compulsoryProgress ?? 0) | 0;
|
||||
const elective = Number(data.electiveProgress ?? 0) | 0;
|
||||
this.progressRing = {
|
||||
total: total,
|
||||
required: required,
|
||||
elective: elective,
|
||||
};
|
||||
} catch (_) {
|
||||
// 默认0
|
||||
this.progressRing = { total: 0, required: 0, elective: 0 };
|
||||
}
|
||||
},
|
||||
async onTypeChange() {
|
||||
await this.loadList();
|
||||
},
|
||||
async onStatusChange() {
|
||||
await this.loadList();
|
||||
},
|
||||
async onPageChange(p) {
|
||||
this.page = p;
|
||||
await this.loadList();
|
||||
},
|
||||
onGuide() {
|
||||
this.guideDialogVisible = true;
|
||||
},
|
||||
toLearn(courseId) {
|
||||
this.$router.push(
|
||||
`/course/studyindex?id=${courseId}&newEmployee=${true}`
|
||||
);
|
||||
},
|
||||
async onDownload() {
|
||||
try {
|
||||
const res = await downloadSocialRecruitReport(
|
||||
this.projectId,
|
||||
this.studentId
|
||||
);
|
||||
const blob = new Blob([res], { type: "application/pdf" });
|
||||
const pdfUrl = URL.createObjectURL(blob);
|
||||
window.open(pdfUrl);
|
||||
} catch (e) {
|
||||
this.$message.error("下载失败");
|
||||
}
|
||||
},
|
||||
async returnEnroll() {
|
||||
this.$confirm("是否重新报名社招新员工项目?", {
|
||||
title: "报名确认",
|
||||
type: "warning",
|
||||
customClass: "returnEnroll",
|
||||
}).then(async () => {
|
||||
const res = await enrollRequest();
|
||||
if (res.data.sendToOaSuccess != 0) {
|
||||
this.$refs.errorMessage.show(
|
||||
"报名失败请稍后重试,如果再次失败,请联系:XXXX,联系方式:XXXX。"
|
||||
);
|
||||
} else {
|
||||
this.$refs.customMessage.show("您已重新报名成功");
|
||||
this.approvalResults = 2;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
const res = await getWelcomeData();
|
||||
// 社招新员工项目标识:0否,1是
|
||||
if (res.data.approvalResults == 1 || !res.data.approvalResults) {
|
||||
this.$refs.customMessage.show("未报名或报名失败,请前往报名页面进行报名");
|
||||
this.$router.push("/new-employee/welcome");
|
||||
}
|
||||
this.guideDialogVisible = this.$route.query?.fromWelcome == 1;
|
||||
// 1报名失败、2审核中、3审核通过、4审核失败
|
||||
this.approvalResults = res.data.approvalResults;
|
||||
await this.initIds();
|
||||
if (!this.projectId || !this.studentId) {
|
||||
this.$message.warning("未获得项目或学员信息,列表可能无法加载");
|
||||
}
|
||||
await Promise.all([this.loadList(), this.loadProcess()]);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 颜色变量近似还原截图 */
|
||||
.ne-study-page {
|
||||
font-family: "PingFang SC", "Helvetica Neue", Arial, "Microsoft YaHei",
|
||||
sans-serif;
|
||||
color: #2b2f36;
|
||||
background: #f4f6f9;
|
||||
}
|
||||
|
||||
.banner-header {
|
||||
background: #0f80ff;
|
||||
}
|
||||
.banner {
|
||||
background: #0f80ff;
|
||||
padding-top: 36px;
|
||||
}
|
||||
.banner-inner {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px 0px;
|
||||
}
|
||||
.title {
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 1200px;
|
||||
margin: 16px auto 40px;
|
||||
padding: 0 16px;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
}
|
||||
.main {
|
||||
flex: 1;
|
||||
}
|
||||
.sidebar {
|
||||
width: 360px;
|
||||
margin-top: -70px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
/* 筛选条 */
|
||||
.filters {
|
||||
padding: 0px 12px 8px;
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
background: #ffffff;
|
||||
border-radius: 8px 8px 0 0;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.04);
|
||||
width: 68%;
|
||||
}
|
||||
.filter-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.label {
|
||||
color: #333333;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 24px 0px 12px;
|
||||
}
|
||||
.pills {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
.pill {
|
||||
padding: 6px 12px;
|
||||
border-radius: 16px;
|
||||
background: #f3f5f8;
|
||||
color: #4b5563;
|
||||
font-size: 12px;
|
||||
cursor: default;
|
||||
}
|
||||
.pill.active {
|
||||
background: #e6f1ff;
|
||||
color: #0f80ff;
|
||||
border: 1px solid #b3d4ff;
|
||||
}
|
||||
|
||||
/* 列表 */
|
||||
.list {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
margin-bottom: 12px;
|
||||
transition: transform 0.08s ease, box-shadow 0.12s ease,
|
||||
background-color 0.12s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
.list-item:hover,
|
||||
.list-item:active {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.06);
|
||||
background-color: #ffffff;
|
||||
}
|
||||
/* 触摸设备:点按反馈 */
|
||||
@media (hover: none) {
|
||||
.list-item:active {
|
||||
transform: scale(0.995);
|
||||
background-color: #f8fbff;
|
||||
}
|
||||
}
|
||||
.item-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.title-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
color: #2b2f36;
|
||||
}
|
||||
.tags {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 22px;
|
||||
padding: 0 8px;
|
||||
border-radius: 12px;
|
||||
font-size: 12px;
|
||||
color: #0f80ff;
|
||||
background: #eaf3ff;
|
||||
}
|
||||
.tag-green {
|
||||
color: #27ce9a;
|
||||
background: #e9fbf3;
|
||||
}
|
||||
.tag-yellow {
|
||||
color: #ffb01e;
|
||||
background: #ffecec;
|
||||
}
|
||||
.tag-blue {
|
||||
color: #0f80ff;
|
||||
background: #eaf3ff;
|
||||
}
|
||||
|
||||
.progress-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.progress {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
background: #eff2f6;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bar {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background: #19c37d;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.w-8 {
|
||||
width: 8%;
|
||||
background: #ffb74d;
|
||||
}
|
||||
.w-40 {
|
||||
width: 40%;
|
||||
background: #2fa3ff;
|
||||
}
|
||||
.w-100 {
|
||||
width: 100%;
|
||||
background: #19c37d;
|
||||
}
|
||||
.percent {
|
||||
font-size: 12px;
|
||||
color: #8a8f99;
|
||||
width: 42px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
min-width: 72px;
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item-right.score {
|
||||
font-weight: 700;
|
||||
}
|
||||
.item-right.score.good {
|
||||
color: #19c37d;
|
||||
}
|
||||
.item-right.score.bad {
|
||||
color: #ff5a5f;
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 0 4px;
|
||||
}
|
||||
.pg {
|
||||
min-width: 30px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
background: #ffffff;
|
||||
color: #4b5563;
|
||||
font-size: 12px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.pg.btn {
|
||||
padding: 0 8px;
|
||||
}
|
||||
.pg.active {
|
||||
background: #0f80ff;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 细化 text 按钮与原页面链接的一致性 */
|
||||
.item-right >>> .el-button--text {
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
.item-right.link >>> .el-button--text {
|
||||
color: #0f80ff;
|
||||
}
|
||||
.item-right.done >>> .el-button--text {
|
||||
color: #8a8f99;
|
||||
}
|
||||
.item-right.score.good >>> .el-button--text {
|
||||
color: #19c37d;
|
||||
}
|
||||
.item-right.score.bad >>> .el-button--text {
|
||||
color: #ff5a5f;
|
||||
}
|
||||
|
||||
/* 单选按钮组胶囊皮肤(替代 Tabs),保持外观一致 */
|
||||
.pill-radios {
|
||||
padding-top: 20px;
|
||||
}
|
||||
.pill-radios >>> .el-radio-button__inner {
|
||||
background: #f3f5f8;
|
||||
color: #4b5563;
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 16px !important;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.pill-radios
|
||||
>>> .el-radio-button__orig-radio:checked
|
||||
+ .el-radio-button__inner {
|
||||
background: #e6f1ff;
|
||||
color: #0f80ff;
|
||||
border-color: #b3d4ff;
|
||||
box-shadow: none;
|
||||
}
|
||||
.pill-radios >>> .el-radio-button__inner:hover {
|
||||
filter: brightness(0.98);
|
||||
}
|
||||
.pill-radios >>> .el-radio-button__inner::before,
|
||||
.pill-radios >>> .el-radio-button__inner::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 侧栏 */
|
||||
.profile {
|
||||
padding: 20px 16px;
|
||||
}
|
||||
.avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background: #d9e8ff;
|
||||
border-radius: 50%;
|
||||
margin: -48px auto 12px;
|
||||
border: 6px solid #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.name {
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
color: #2b2f36;
|
||||
}
|
||||
.desc {
|
||||
text-align: center;
|
||||
color: #8a8f99;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.btns {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
margin-top: 14px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.btn {
|
||||
width: 100% !important;
|
||||
height: 36px;
|
||||
padding: 0 14px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn-outline {
|
||||
background: rgba(0, 120, 252, 1);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(0, 120, 252, 1);
|
||||
width: 120px;
|
||||
}
|
||||
.transcript {
|
||||
text-align: center;
|
||||
}
|
||||
.transcript .label {
|
||||
color: #8a8f99;
|
||||
font-size: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.btn-long {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
border-radius: 8px;
|
||||
background: #1ac07f;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2px 8px rgba(26, 192, 127, 0.35);
|
||||
}
|
||||
.btn-long.btn-disabled,
|
||||
.btn-long:disabled {
|
||||
background: #cccccc !important;
|
||||
color: #ffffff !important;
|
||||
cursor: not-allowed !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.btn-long.btn-disabled:hover,
|
||||
.btn-long:disabled:hover {
|
||||
background: #cccccc !important;
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
.progress-circles {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.circle {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 32%;
|
||||
}
|
||||
.ring {
|
||||
width: 84px;
|
||||
height: 84px;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(#d9dfe7 0 65%, #eef2f6 65%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #4b5563;
|
||||
font-weight: 700;
|
||||
}
|
||||
.ring span {
|
||||
background: #ffffff;
|
||||
width: 66px;
|
||||
height: 66px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: inset 0 0 0 6px #ffffff;
|
||||
}
|
||||
.circle .text {
|
||||
font-size: 12px;
|
||||
color: #8a8f99;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.returnEnroll {
|
||||
width: 380px;
|
||||
height: auto;
|
||||
.el-message-box__header {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 15px 15px 10px;
|
||||
.el-message-box__title {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.el-message-box__content {
|
||||
padding: 10px 15px;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
text-align: left;
|
||||
font-weight: 500;
|
||||
margin-top: 0px;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB,
|
||||
Microsoft YaHei, SimSun, sans-serif;
|
||||
}
|
||||
.el-message-box__status {
|
||||
display: block;
|
||||
}
|
||||
.el-message-box__btns {
|
||||
padding: 5px 15px 0;
|
||||
text-align: right;
|
||||
margin-top: 0px;
|
||||
.el-button {
|
||||
padding: 9px 15px;
|
||||
font-size: 12px;
|
||||
border-radius: 3px;
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
.el-message-box__btns button:nth-child(2) {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
<template>
|
||||
<div class="welcome" v-loading="enrollLoading">
|
||||
<p class="top-title">欢迎来到京东方大学</p>
|
||||
<!-- 内容卡片 -->
|
||||
<div class="main">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<img
|
||||
class="img-responsive wel_img"
|
||||
src="@/assets/images/new-employee/wel.png"
|
||||
alt=""
|
||||
/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<!-- 桌面端显示 -->
|
||||
<div class="right-font visible-lg-block visible-md-block">
|
||||
<p class="greeting">{{ userInfo.name }},您好!</p>
|
||||
<p>欢迎加入京东方科技集团股份有限公司</p>
|
||||
<p>从这一刻起,你就是这个大家庭里的一份子。</p>
|
||||
<p>在这里,我们干事业、交朋友、长本领、练胸怀!</p>
|
||||
<p>为了帮你快速融入BOE,我们为你准备了丰富的入职培训课程!</p>
|
||||
<p>快开始学习吧!</p>
|
||||
</div>
|
||||
<!-- 移动端显示 -->
|
||||
<div class="right-font-sm visible-xs-block">
|
||||
<p>{{ userInfo.name }},您好!</p>
|
||||
<p>欢迎加入京东方</p>
|
||||
<p>为了帮你快速融入BOE</p>
|
||||
<P>我们为你准备了丰富的入职培训课程</P>
|
||||
<p>快开始学习吧!</p>
|
||||
</div>
|
||||
<div class="col-md-12 send_btn">
|
||||
<button
|
||||
v-if="approvalResults == '1' || approvalResults == '4'"
|
||||
class="btn btn-default btn-lg send center-block start-btn"
|
||||
:disabled="enrollLoading"
|
||||
@click="handleEnrollClick"
|
||||
>
|
||||
报名社招新员工项目
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-default btn-lg send center-block start-btn"
|
||||
:disabled="enrollLoading"
|
||||
@click="handleEnrollClick"
|
||||
>
|
||||
开启学习之旅
|
||||
</button>
|
||||
<p
|
||||
style="
|
||||
color: #ffe551;
|
||||
font-size: 14px;
|
||||
font-family: '黑体';
|
||||
margin-top: 20px;
|
||||
"
|
||||
>
|
||||
温馨提示:仅社招新员工可以报名,校招新员工和其他员工请不要点击
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 学习引导页面 -->
|
||||
<!-- <NewEmployeeGuideDialog
|
||||
:visible.sync="infoConfirmDialogVisible"
|
||||
:processing="processing"
|
||||
:onConfirm="handleConfirm"
|
||||
:onCancel="() => (infoConfirmDialogVisible = false)"
|
||||
@close="handleDialogClose"
|
||||
/> -->
|
||||
<!-- 自定义信息提示 -->
|
||||
<CustomErrorMessage ref="customMessage" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import { getWelcomeData, enrollRequest } from "@/api/new-employee/newEmployee";
|
||||
import NewEmployeeGuideDialog from "@/components/NewEmployeeGuideDialog.vue";
|
||||
import CustomErrorMessage from "@/components/CustomErrorMessage.vue";
|
||||
|
||||
export default {
|
||||
name: "WelcomePage",
|
||||
components: { NewEmployeeGuideDialog, CustomErrorMessage },
|
||||
data() {
|
||||
return {
|
||||
userName: "",
|
||||
projectId: "",
|
||||
approvalResults: 1,
|
||||
processing: false,
|
||||
confirmBtnText: "确认",
|
||||
infoConfirmDialogVisible: false,
|
||||
enrollLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// 如果需要使用Vuex中的用户信息
|
||||
...mapGetters(["userInfo"]),
|
||||
},
|
||||
mounted() {
|
||||
this.initPageData();
|
||||
},
|
||||
methods: {
|
||||
// 初始化页面数据
|
||||
async initPageData() {
|
||||
try {
|
||||
// 从后端获取用户信息和报名状态
|
||||
const res = await getWelcomeData();
|
||||
if (res.status === 200 && res.data) {
|
||||
this.userName = res.data.studentName;
|
||||
// 1报名失败、2审核中、3审核通过、4审核失败
|
||||
if (res.data.approvalResults) {
|
||||
this.approvalResults = res.data.approvalResults;
|
||||
if (res.data.approvalResults != 1) {
|
||||
this.$router.push("/new-employee/study");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("初始化数据失败:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// 报名按钮点击
|
||||
async handleEnrollClick() {
|
||||
if (this.approvalResults == "1" || this.approvalResults == "4") {
|
||||
this.enrollLoading = true;
|
||||
try {
|
||||
const res = await enrollRequest();
|
||||
if (res.data.data.sendToOaSuccess != 0) {
|
||||
this.$refs.customMessage.show(
|
||||
"报名失败请稍后重试,如果再次失败,请联系:XXXX,联系方式:XXXX。"
|
||||
);
|
||||
} else {
|
||||
this.$router.push("/new-employee/study?fromWelcome=1");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("报名请求失败:", error);
|
||||
this.$refs.customMessage.show(
|
||||
"报名失败请稍后重试,如果再次失败,请联系:XXXX,联系方式:XXXX。"
|
||||
);
|
||||
} finally {
|
||||
this.enrollLoading = false;
|
||||
}
|
||||
} else {
|
||||
this.$router.push("/new-employee/study?fromWelcome=1");
|
||||
}
|
||||
},
|
||||
|
||||
// 确认按钮
|
||||
// async handleConfirm() {
|
||||
// try {
|
||||
// this.$router.push("/new-employee/study");
|
||||
// } catch (error) {
|
||||
// console.error("提交失败:", error);
|
||||
// this.processing = false;
|
||||
// this.confirmBtnText = "确认";
|
||||
// this.$message.error("提交失败,请重试");
|
||||
// }
|
||||
// },
|
||||
|
||||
// 关闭对话框
|
||||
handleDialogClose() {
|
||||
this.processing = false;
|
||||
this.confirmBtnText = "确认";
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 欢迎页面样式 */
|
||||
.welcome {
|
||||
min-height: 100vh;
|
||||
background: #2e5aa6;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 顶部标题 */
|
||||
.top-title {
|
||||
/* 左移整个屏幕的1/4 */
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
text-align: center;
|
||||
margin: 40px 20px 60px;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
margin-left: -25%;
|
||||
}
|
||||
|
||||
/* 主内容卡片 */
|
||||
.welcome .main {
|
||||
background: #2e5aa6;
|
||||
border-radius: 10px;
|
||||
padding: 50px 40px;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 自定义栅格系统 */
|
||||
.welcome .main .row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0 -15px;
|
||||
}
|
||||
|
||||
.welcome .main .col-md-6 {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 0 15px;
|
||||
flex: 0 0 50%;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
.welcome .main .col-md-12 {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 0 15px;
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.wel_img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* 右侧文字区域 */
|
||||
.right-font {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.right-font .greeting {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
margin-bottom: 15px;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.right-font p {
|
||||
font-size: 18px;
|
||||
line-height: 2;
|
||||
color: white;
|
||||
margin-bottom: 10px;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.right-font-sm p {
|
||||
font-size: 16px;
|
||||
line-height: 1.8;
|
||||
color: white;
|
||||
margin-bottom: 8px;
|
||||
font-family: "Microsoft YaHei", sans-serif;
|
||||
}
|
||||
|
||||
.send_btn {
|
||||
margin-top: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
/* background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); */
|
||||
background: #2e5aa6;
|
||||
border: 5px solid #6e88b5;
|
||||
color: white;
|
||||
padding: 15px 50px;
|
||||
font-size: 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
/* box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); */
|
||||
}
|
||||
|
||||
.start-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
/* box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
|
||||
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); */
|
||||
}
|
||||
|
||||
.start-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.start-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.start-btn:disabled:hover {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 响应式样式 */
|
||||
@media (max-width: 768px) {
|
||||
.top-title {
|
||||
font-size: 32px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
padding: 40px 15px 30px;
|
||||
}
|
||||
|
||||
.welcome .main {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
.welcome .main .col-md-6 {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.right-font p {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.start-btn {
|
||||
padding: 12px 30px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 显示/隐藏控制 */
|
||||
.visible-lg-block,
|
||||
.visible-md-block,
|
||||
.visible-xs-block {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.visible-lg-block,
|
||||
.visible-md-block {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) and (max-width: 991px) {
|
||||
.visible-xs-block {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.visible-xs-block {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading遮罩层透明度 */
|
||||
.welcome ::v-deep .el-loading-mask {
|
||||
background-color: rgba(255, 255, 255, 0.5) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
873
src/views/portal/case/AICall.vue
Normal file
@@ -0,0 +1,873 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 最大化状态的弹窗 -->
|
||||
<el-dialog
|
||||
v-show=" windowState === 'maximized'"
|
||||
v-if="dialogVisible"
|
||||
:visible="true"
|
||||
:close-on-click-modal="false"
|
||||
:show-close="true"
|
||||
@close="onClose"
|
||||
class="case-expert-dialog"
|
||||
:modal="false"
|
||||
:append-to-body="true"
|
||||
:fullscreen="false"
|
||||
top="10vh"
|
||||
v-resizeable
|
||||
v-draggable
|
||||
>
|
||||
<!-- 标题 -->
|
||||
<div slot="title" class="dialog-title">
|
||||
<span>案例专家</span>
|
||||
<el-button
|
||||
style="color:#96999f"
|
||||
type="text"
|
||||
class="window-control-btn"
|
||||
@click="minimizeWindow"
|
||||
>
|
||||
<i class="el-icon-minus"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="content-wrapper">
|
||||
<div
|
||||
class="welcome-message"
|
||||
ref="messageContainer"
|
||||
@scroll="handleScroll"
|
||||
>
|
||||
<div class="message-text" v-for="(item, index) in messageList" :key="index">
|
||||
<messages :messageData="item" :suggestions="suggestions" @getMinWindow="minimizeWindow"></messages>
|
||||
</div>
|
||||
<div class="message-suggestions" v-if="messageList.length > 0 && messageList[messageList.length-1].textCompleted">
|
||||
<div class="suggestion-item" v-for="(item, index) in suggestions" :key="index">
|
||||
<a @click="sendSuggestions(item)"> {{ item }} →</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isLoading" class="loading-message">
|
||||
<div class="loading-dots">
|
||||
<span></span>
|
||||
<span></span>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入框区域 -->
|
||||
<send-message
|
||||
v-model="AIContent"
|
||||
:message-list="messageList"
|
||||
:suggestions="suggestions"
|
||||
@loading="handleLoading"
|
||||
@update-message="updateMessage"
|
||||
@update-suggestions="updateSuggestions"
|
||||
@new-conversation="startNewConversation"
|
||||
:disabled="isLoading"
|
||||
class="input-area-wrapper"
|
||||
ref="sendMessage"
|
||||
/>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 最小化状态的弹窗 -->
|
||||
<div
|
||||
class="minimized-window"
|
||||
v-show="windowState === 'minimized' && showMinimizedWindow"
|
||||
@click="onMinimizedWindowClick"
|
||||
>
|
||||
<div class="minimized-content">
|
||||
<span class="window-title">案例专家</span>
|
||||
<div style="display: flex;align-items: center">
|
||||
<el-button
|
||||
type="text"
|
||||
class="window-control-btn"
|
||||
@click.stop="onMinimizedWindowClick"
|
||||
>
|
||||
<img :src="openImg" alt="" style="width: 17px">
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
style="margin-left: 1px;color:#96999f"
|
||||
type="text"
|
||||
class="window-control-btn"
|
||||
@click.stop="closeMinimizedWindow"
|
||||
>
|
||||
<i class="el-icon-close"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="minimized-message">
|
||||
<div v-if="messageList.length <= 1 && messageList[0].isBot">
|
||||
当前暂无对话内容,去创建对话吧
|
||||
</div>
|
||||
<div v-else>
|
||||
{{ getLastUserMessage() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex'
|
||||
import messages from './components/messages.vue'
|
||||
import sendMessage from './components/sendMessage.vue'
|
||||
import openImg from './components/open.png'
|
||||
export default {
|
||||
name: 'CaseExpertDialog',
|
||||
props: {
|
||||
dialogVisible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
components: {
|
||||
messages,
|
||||
sendMessage
|
||||
},
|
||||
directives: {
|
||||
draggable: {
|
||||
bind(el, binding, vnode) {
|
||||
vnode.context.$nextTick(() => {
|
||||
const dialogEl = el.querySelector('.el-dialog');
|
||||
if (!dialogEl) return;
|
||||
|
||||
const headerEl = dialogEl.querySelector('.dialog-title');
|
||||
if (!headerEl) return;
|
||||
|
||||
// 检查是否有保存的位置状态
|
||||
const savedPosition = sessionStorage.getItem('aiCallDialogPosition');
|
||||
if (savedPosition) {
|
||||
const { left, top } = JSON.parse(savedPosition);
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
} else {
|
||||
// 设置初始样式
|
||||
dialogEl.style.position = 'fixed';
|
||||
dialogEl.style.top = '100px';
|
||||
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
|
||||
}
|
||||
dialogEl.style.margin = '0';
|
||||
|
||||
let isDragging = false;
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let startLeft = 0;
|
||||
let startTop = 0;
|
||||
|
||||
const startDrag = (event) => {
|
||||
// 只有在标题栏上按下鼠标才开始拖动
|
||||
if (event.target.closest('.resize-handle')) {
|
||||
return; // 如果点击的是resize-handle,则不触发拖动
|
||||
}
|
||||
|
||||
isDragging = true;
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
startLeft = parseInt(dialogEl.style.left) || dialogEl.offsetLeft;
|
||||
startTop = parseInt(dialogEl.style.top) || dialogEl.offsetTop;
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// 添加全局事件监听
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
if (!isDragging) return;
|
||||
|
||||
const deltaX = event.clientX - startX;
|
||||
const deltaY = event.clientY - startY;
|
||||
|
||||
dialogEl.style.left = (startLeft + deltaX) + 'px';
|
||||
dialogEl.style.top = (startTop + deltaY) + 'px';
|
||||
};
|
||||
|
||||
const stopDrag = () => {
|
||||
isDragging = false;
|
||||
|
||||
// 保存当前位置到 sessionStorage
|
||||
const currentPosition = {
|
||||
left: parseInt(dialogEl.style.left),
|
||||
top: parseInt(dialogEl.style.top)
|
||||
};
|
||||
sessionStorage.setItem('aiCallDialogPosition', JSON.stringify(currentPosition));
|
||||
|
||||
// 移除全局事件监听
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', stopDrag);
|
||||
};
|
||||
|
||||
// 为标题栏绑定拖动事件
|
||||
headerEl.addEventListener('mousedown', startDrag);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
resizeable: {
|
||||
bind(el, binding, vnode) {
|
||||
// 确保元素已插入DOM
|
||||
vnode.context.$nextTick(() => {
|
||||
const dialogEl = el.querySelector('.el-dialog');
|
||||
if (!dialogEl) return;
|
||||
|
||||
// 检查是否有保存的尺寸状态
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
if (savedSize) {
|
||||
const { width, height, left, top } = JSON.parse(savedSize);
|
||||
dialogEl.style.width = width + 'px';
|
||||
dialogEl.style.height = height + 'px';
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
} else {
|
||||
// 设置初始样式
|
||||
dialogEl.style.position = 'fixed';
|
||||
dialogEl.style.top = '100px';
|
||||
dialogEl.style.left = (window.innerWidth - dialogEl.offsetWidth) / 2 + 'px';
|
||||
}
|
||||
|
||||
// 创建拖拽手柄
|
||||
const createHandle = (direction) => {
|
||||
const handle = document.createElement('div');
|
||||
handle.className = `resize-handle ${direction}`;
|
||||
handle.style.position = 'absolute';
|
||||
handle.style.zIndex = '10';
|
||||
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
case 'right':
|
||||
handle.style.width = '6px';
|
||||
handle.style.height = '100%';
|
||||
handle.style.top = '0';
|
||||
handle.style.cursor = 'ew-resize';
|
||||
break;
|
||||
case 'top':
|
||||
case 'bottom':
|
||||
handle.style.width = '100%';
|
||||
handle.style.height = '6px';
|
||||
handle.style.left = '0';
|
||||
handle.style.cursor = 'ns-resize';
|
||||
break;
|
||||
case 'top-left':
|
||||
case 'top-right':
|
||||
case 'bottom-left':
|
||||
case 'bottom-right':
|
||||
handle.style.width = '10px';
|
||||
handle.style.height = '10px';
|
||||
handle.style.zIndex = '20';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
handle.style.left = '0';
|
||||
break;
|
||||
case 'right':
|
||||
handle.style.right = '0';
|
||||
break;
|
||||
case 'top':
|
||||
handle.style.top = '0';
|
||||
break;
|
||||
case 'bottom':
|
||||
handle.style.bottom = '0';
|
||||
break;
|
||||
case 'top-left':
|
||||
handle.style.top = '0';
|
||||
handle.style.left = '0';
|
||||
handle.style.cursor = 'nw-resize';
|
||||
break;
|
||||
case 'top-right':
|
||||
handle.style.top = '0';
|
||||
handle.style.right = '0';
|
||||
handle.style.cursor = 'ne-resize';
|
||||
break;
|
||||
case 'bottom-left':
|
||||
handle.style.bottom = '0';
|
||||
handle.style.left = '0';
|
||||
handle.style.cursor = 'sw-resize';
|
||||
break;
|
||||
case 'bottom-right':
|
||||
handle.style.bottom = '0';
|
||||
handle.style.right = '0';
|
||||
handle.style.cursor = 'se-resize';
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 防止拖拽手柄的事件冒泡到标题栏
|
||||
handle.addEventListener('mousedown', (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
});
|
||||
|
||||
dialogEl.appendChild(handle);
|
||||
return handle;
|
||||
};
|
||||
|
||||
// 创建8个拖拽手柄
|
||||
const handles = {
|
||||
left: createHandle('left'),
|
||||
right: createHandle('right'),
|
||||
top: createHandle('top'),
|
||||
bottom: createHandle('bottom'),
|
||||
topLeft: createHandle('top-left'),
|
||||
topRight: createHandle('top-right'),
|
||||
bottomLeft: createHandle('bottom-left'),
|
||||
bottomRight: createHandle('bottom-right')
|
||||
};
|
||||
|
||||
// 添加拖拽事件处理
|
||||
let isResizing = false;
|
||||
let resizeDirection = '';
|
||||
let startX = 0;
|
||||
let startY = 0;
|
||||
let startWidth = 0;
|
||||
let startHeight = 0;
|
||||
let startLeft = 0;
|
||||
let startTop = 0;
|
||||
|
||||
const startResize = (direction, event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
isResizing = true;
|
||||
resizeDirection = direction;
|
||||
|
||||
startX = event.clientX;
|
||||
startY = event.clientY;
|
||||
startWidth = dialogEl.offsetWidth;
|
||||
startHeight = dialogEl.offsetHeight;
|
||||
|
||||
// 统一使用计算后的样式值
|
||||
startLeft = parseInt(dialogEl.style.left) || 0;
|
||||
startTop = parseInt(dialogEl.style.top) || 0;
|
||||
|
||||
// 添加全局事件监听
|
||||
document.addEventListener('mousemove', handleMouseMove);
|
||||
document.addEventListener('mouseup', stopResize);
|
||||
};
|
||||
|
||||
const handleMouseMove = (event) => {
|
||||
if (!isResizing) return;
|
||||
|
||||
const deltaX = event.clientX - startX;
|
||||
const deltaY = event.clientY - startY;
|
||||
|
||||
let newWidth, newHeight, newLeft, newTop;
|
||||
|
||||
switch (resizeDirection) {
|
||||
case 'right':
|
||||
newWidth = Math.max(400, startWidth + deltaX);
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
break;
|
||||
case 'left':
|
||||
newWidth = Math.max(400, startWidth - deltaX);
|
||||
newLeft = startLeft + startWidth - newWidth;
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.left = newLeft + 'px';
|
||||
break;
|
||||
case 'bottom':
|
||||
newHeight = Math.max(600, startHeight + deltaY);
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
break;
|
||||
case 'top':
|
||||
// 当窗口高度达到最小值时,不再调整高度和位置
|
||||
if (startHeight - deltaY >= 600) {
|
||||
newHeight = startHeight - deltaY;
|
||||
newTop = startTop + deltaY;
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.top = newTop + 'px';
|
||||
}
|
||||
break;
|
||||
case 'bottom-right':
|
||||
newWidth = Math.max(400, startWidth + deltaX);
|
||||
newHeight = Math.max(600, startHeight + deltaY);
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
break;
|
||||
case 'bottom-left':
|
||||
newWidth = Math.max(400, startWidth - deltaX);
|
||||
newHeight = Math.max(600, startHeight + deltaY);
|
||||
newLeft = startLeft + startWidth - newWidth;
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.left = newLeft + 'px';
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
break;
|
||||
case 'top-right':
|
||||
// 当窗口高度达到最小值时,不再调整高度和位置
|
||||
if (startHeight - deltaY >= 600) {
|
||||
newHeight = startHeight - deltaY;
|
||||
newTop = startTop + deltaY;
|
||||
newWidth = Math.max(400, startWidth + deltaX);
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.top = newTop + 'px';
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
}
|
||||
break;
|
||||
case 'top-left':
|
||||
// 当窗口高度达到最小值时,不再调整高度和位置
|
||||
if (startHeight - deltaY >= 600) {
|
||||
newHeight = startHeight - deltaY;
|
||||
newTop = startTop + deltaY;
|
||||
newWidth = Math.max(400, startWidth - deltaX);
|
||||
newLeft = startLeft + startWidth - newWidth;
|
||||
dialogEl.style.height = newHeight + 'px';
|
||||
dialogEl.style.top = newTop + 'px';
|
||||
dialogEl.style.width = newWidth + 'px';
|
||||
dialogEl.style.left = newLeft + 'px';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
let doc = document.querySelector('.welcome-message')
|
||||
let sendBox = document.querySelector('.input-area-wrapper');
|
||||
// sendBox 的高度
|
||||
if (doc && sendBox) {
|
||||
doc.style.height = `calc(${dialogEl.style.height} - ${sendBox.offsetHeight}px - 120px)`;
|
||||
}
|
||||
};
|
||||
|
||||
const stopResize = () => {
|
||||
isResizing = false;
|
||||
resizeDirection = '';
|
||||
|
||||
// 保存当前尺寸和位置到 sessionStorage
|
||||
const currentSize = {
|
||||
width: parseInt(dialogEl.style.width),
|
||||
height: parseInt(dialogEl.style.height),
|
||||
left: parseInt(dialogEl.style.left),
|
||||
top: parseInt(dialogEl.style.top)
|
||||
};
|
||||
sessionStorage.setItem('aiCallDialogSize', JSON.stringify(currentSize));
|
||||
|
||||
// 移除全局事件监听
|
||||
document.removeEventListener('mousemove', handleMouseMove);
|
||||
document.removeEventListener('mouseup', stopResize);
|
||||
};
|
||||
|
||||
// 为每个手柄绑定事件
|
||||
handles.left.addEventListener('mousedown', (e) => startResize('left', e));
|
||||
handles.right.addEventListener('mousedown', (e) => startResize('right', e));
|
||||
handles.top.addEventListener('mousedown', (e) => startResize('top', e));
|
||||
handles.bottom.addEventListener('mousedown', (e) => startResize('bottom', e));
|
||||
handles.topLeft.addEventListener('mousedown', (e) => startResize('top-left', e));
|
||||
handles.topRight.addEventListener('mousedown', (e) => startResize('top-right', e));
|
||||
handles.bottomLeft.addEventListener('mousedown', (e) => startResize('bottom-left', e));
|
||||
handles.bottomRight.addEventListener('mousedown', (e) => startResize('bottom-right', e));
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState('app', ['showAICallMinimized']),
|
||||
showMinimizedWindow() {
|
||||
// 只有在Vuex状态为true时才显示最小化窗口
|
||||
return this.showAICallMinimized;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
openImg,
|
||||
AIContent: '',
|
||||
isLoading: false,
|
||||
windowState: 'maximized', // 'maximized' 或 'minimized'
|
||||
messageList: [
|
||||
{
|
||||
typing:true,
|
||||
isBot: true, // 是否为机器人
|
||||
text: `<p><b>您好!我是京东方案例智能问答助手,随时为您服务。</b></p>
|
||||
<p>我可以帮您快速查找和解读平台内的各类案例内容。只需输入您想了解的问题或关键词,我会从案例库中精准匹配相关信息,并提供清晰的解答。每条回答都会附上来源链接,方便您随时查阅原始案例全文。</p>
|
||||
<p>我还会根据您的提问,智能推荐相关延伸问题,助您更高效地探索知识、解决问题。</p>
|
||||
<p>现在,欢迎随时向我提问,开启高效的知识查询体验吧!</p>`
|
||||
}
|
||||
],
|
||||
suggestions:[],
|
||||
isAutoScroll: true, // 是否自动滚动
|
||||
// 添加一个标志位,用于标识组件是否已经初始化完成
|
||||
isComponentReady: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 组件挂载完成后,标记为已准备就绪
|
||||
this.$nextTick(() => {
|
||||
this.isComponentReady = true;
|
||||
});
|
||||
},
|
||||
watch: {
|
||||
dialogVisible: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.$nextTick(() => {
|
||||
// 获取对话框元素
|
||||
const dialogEl = document.querySelector('.case-expert-dialog .el-dialog');
|
||||
if (dialogEl) {
|
||||
// 检查是否有保存的尺寸状态
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
if (savedSize) {
|
||||
const { width, height, left, top } = JSON.parse(savedSize);
|
||||
dialogEl.style.width = width + 'px';
|
||||
dialogEl.style.height = height + 'px';
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
}
|
||||
|
||||
// 检查是否有保存的位置状态
|
||||
const savedPosition = sessionStorage.getItem('aiCallDialogPosition');
|
||||
if (savedPosition) {
|
||||
const { left, top } = JSON.parse(savedPosition);
|
||||
dialogEl.style.left = left + 'px';
|
||||
dialogEl.style.top = top + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
let doc = document.querySelector('.welcome-message')
|
||||
let sendBox = document.querySelector('.input-area-wrapper');
|
||||
// 只有在没有保存的尺寸状态时才使用默认值
|
||||
if (doc && sendBox) {
|
||||
const savedSize = sessionStorage.getItem('aiCallDialogSize');
|
||||
if (!savedSize) {
|
||||
doc.style.height = `calc(600px - ${sendBox.offsetHeight}px - 120px)`;
|
||||
} else {
|
||||
const { height } = JSON.parse(savedSize);
|
||||
doc.style.height = `calc(${height}px - ${sendBox.offsetHeight}px - 120px)`;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
messageList: {
|
||||
handler() {
|
||||
// 只有在组件准备就绪后才执行滚动操作
|
||||
if (this.isComponentReady) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom();
|
||||
});
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// / 关闭最小化窗口
|
||||
closeMinimizedWindow() {
|
||||
this.$store.commit('app/SET_SHOW_AI_CALL_MINIMIZED', false);
|
||||
this.$store.commit('app/SET_SHOW_AI_CALL', false);
|
||||
this.windowState = 'maximized';
|
||||
},
|
||||
getMinWidow(vis){
|
||||
// this.showAICallMinimized = vis
|
||||
this.windowState = 'minimized';
|
||||
},
|
||||
onClose() {
|
||||
console.log('关闭弹窗')
|
||||
// 清除保存的状态
|
||||
sessionStorage.removeItem('aiCallDialogSize');
|
||||
sessionStorage.removeItem('aiCallDialogPosition');
|
||||
this.$emit('close')
|
||||
// 可以在这里执行其他逻辑
|
||||
},
|
||||
|
||||
minimizeWindow() {
|
||||
this.windowState = 'minimized';
|
||||
this.$store.commit('app/SET_SHOW_AI_CALL_MINIMIZED', true);
|
||||
},
|
||||
|
||||
maximizeWindow() {
|
||||
this.windowState = 'maximized';
|
||||
},
|
||||
|
||||
getLastUserMessage() {
|
||||
// 从后往前找用户消息
|
||||
for (let i = this.messageList.length - 1; i >= 0; i--) {
|
||||
if (!this.messageList[i].isBot) {
|
||||
// 移除HTML标签只返回纯文本
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = this.messageList[i].text;
|
||||
return tempDiv.textContent || tempDiv.innerText || '';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
// 处理加载状态
|
||||
handleLoading(status) {
|
||||
this.isLoading = status;
|
||||
},
|
||||
|
||||
// 更新消息
|
||||
updateMessage(message) {
|
||||
// 由于Vue的响应式系统,message对象的更改会自动更新视图
|
||||
// 这里不需要额外的操作
|
||||
},
|
||||
updateSuggestions(arr){
|
||||
this.suggestions = arr
|
||||
},
|
||||
// 处理建议
|
||||
sendSuggestions(item){
|
||||
// this.suggestions = []
|
||||
this.AIContent = item
|
||||
setTimeout(()=>{
|
||||
this.$refs.sendMessage.handleSend()
|
||||
this.AIContent = ''
|
||||
},500)
|
||||
},
|
||||
startNewConversation() {
|
||||
// 重置对话时,先标记组件为未准备就绪状态
|
||||
this.isComponentReady = false;
|
||||
|
||||
this.messageList = [
|
||||
{
|
||||
isBot: true,
|
||||
text: `<p><b>您好!我是京东方案例智能问答助手,随时为您服务。</b></p>
|
||||
<p>我可以帮您快速查找和解读平台内的各类案例内容。只需输入您想了解的问题或关键词,我会从案例库中精准匹配相关信息,并提供清晰的解答。每条回答都会附上来源链接,方便您随时查阅原始案例全文。</p>
|
||||
<p>我还会根据您的提问,智能推荐相关延伸问题,助您更高效地探索知识、解决问题。</p>
|
||||
<p>现在,欢迎随时向我提问,开启高效的知识查询体验吧!</p>`
|
||||
}
|
||||
];
|
||||
this.AIContent = '';
|
||||
this.isLoading = false;
|
||||
|
||||
// 在下一个 tick 中重新标记为准备就绪
|
||||
this.$nextTick(() => {
|
||||
this.isComponentReady = true;
|
||||
});
|
||||
},
|
||||
|
||||
// 处理滚动事件
|
||||
handleScroll(event) {
|
||||
const element = event.target;
|
||||
// 判断是否滚动到底部
|
||||
const isAtBottom = element.scrollHeight - element.scrollTop <= element.clientHeight + 1;
|
||||
|
||||
// 如果滚动到底部,则开启自动滚动
|
||||
// 如果离开底部,则关闭自动滚动
|
||||
this.isAutoScroll = isAtBottom;
|
||||
},
|
||||
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
if (this.isAutoScroll && this.$refs.messageContainer) {
|
||||
this.$refs.messageContainer.scrollTop = this.$refs.messageContainer.scrollHeight;
|
||||
}
|
||||
},
|
||||
|
||||
// 最小化窗口的点击事件处理方法
|
||||
onMinimizedWindowClick() {
|
||||
// 当点击最小化窗口时,如果dialogVisible为false,则通过事件通知父组件显示对话框
|
||||
if (!this.dialogVisible) {
|
||||
this.$emit('restore');
|
||||
}
|
||||
// 然后将窗口状态设置为最大化
|
||||
this.windowState = 'maximized';
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.case-expert-dialog {
|
||||
::v-deep .el-dialog{
|
||||
background: url("./components/u762.svg") no-repeat ;
|
||||
background-size: cover;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
//background-color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
::v-deep .el-dialog__body{
|
||||
padding: 10px;
|
||||
flex:1;
|
||||
//font-size: 12px;
|
||||
*{
|
||||
font-size:unset ;
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-title {
|
||||
background: transparent;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
padding-right: 20px;
|
||||
cursor: move; /* 添加拖动样式 */
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.window-control-btn {
|
||||
font-size: 18px;
|
||||
padding: 5px 10px;
|
||||
color: #333; /* 黑色图标 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.message-suggestions{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.suggestion-item{
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
padding: 5px 15px;
|
||||
box-sizing: border-box;
|
||||
background-color: rgba(228, 231, 237, 1);
|
||||
border-radius: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
.content-wrapper {
|
||||
padding: 20px;
|
||||
background-color: transparent;
|
||||
border-radius: 8px;
|
||||
min-height: 500px;
|
||||
height:100%;
|
||||
position: relative;
|
||||
//margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.welcome-message {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
height: 400px;
|
||||
//flex:1;
|
||||
overflow-y: auto;
|
||||
|
||||
.avatar {
|
||||
margin-right: 12px;
|
||||
img {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: #007aff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.message-text {
|
||||
width: 100%;
|
||||
//margin-bottom: 15px;
|
||||
|
||||
p {
|
||||
color: #333;
|
||||
//font-size: 14px;
|
||||
line-height: 1.6;
|
||||
margin: 8px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background-color: #007aff;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.loading-dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #999;
|
||||
margin-right: 5px;
|
||||
animation: loading 1.4s infinite ease-in-out both;
|
||||
|
||||
&:nth-child(1) {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input-area-wrapper {
|
||||
//position: absolute;
|
||||
//bottom: 10px;
|
||||
//width: calc(100% - 40px);
|
||||
}
|
||||
|
||||
@keyframes loading {
|
||||
0%, 80%, 100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.minimized-window {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
bottom: 20px;
|
||||
width: 300px;
|
||||
background: url("./components/u762.svg") no-repeat;
|
||||
background-size: cover;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 2000;
|
||||
cursor: pointer;
|
||||
|
||||
.minimized-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
.window-title {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.window-control-btn {
|
||||
font-size: 16px;
|
||||
padding: 3px 8px;
|
||||
color: #000000; /* 黑色图标 */
|
||||
}
|
||||
}
|
||||
|
||||
.minimized-message {
|
||||
padding: 15px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div id="case-list-content">
|
||||
<div style="margin-bottom:30px" class="case-banner">
|
||||
<portal-header current="case" textColor="#fff" :goSearch="2"></portal-header>
|
||||
<portal-header current="case" textColor="#fff" :goSearch="2">
|
||||
|
||||
</portal-header>
|
||||
</div>
|
||||
<div class="">
|
||||
<div class="xcontent2">
|
||||
@@ -109,6 +111,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="xcontent2-minor" :style="{ display: zoomShow ? '' : 'none' }">
|
||||
<AICaseConsult />
|
||||
<div id="fixd-box">
|
||||
<router-link class="the_charts" to="/case/charts">
|
||||
<div class="text">排行榜</div>
|
||||
@@ -237,9 +240,10 @@ import { formatDate } from "@/utils/datetime.js"
|
||||
import { cutFullName } from "@/utils/tools.js";
|
||||
import apiPlace from "@/api/phase2/place.js"
|
||||
import portalFloatTools from "@/components/PortalFloatTools.vue";
|
||||
import AICaseConsult from "@/views/portal/case/components/AICaseConsult.vue";
|
||||
export default {
|
||||
name: 'atticle',
|
||||
components: { portalHeader, portalFloatTools, portalFooter, interactBar, author, comments, pdfPreview },
|
||||
components: {AICaseConsult, portalHeader, portalFloatTools, portalFooter, interactBar, author, comments, pdfPreview },
|
||||
computed: {
|
||||
...mapGetters(['userInfo'])
|
||||
},
|
||||
|
||||
63
src/views/portal/case/components/AICaseConsult.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="AI-case" style="position: relative; margin-bottom: 10px;" v-if="showAiCase" @click.stop="getAICase()">
|
||||
<img src="../../../../../public/images/case-logo.png" alt="">
|
||||
<span @click="getAICase()" style="position: absolute; bottom: 65px;left: 15px;z-index: 1;width: 40%;height: 30px;"></span>
|
||||
</div>
|
||||
<!-- 移除直接使用的AICall组件 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { showCaseAiEntrance } from '@/api/boe/aiChat.js'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'AICaseConsult',
|
||||
data() {
|
||||
return {
|
||||
showAiCase: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 从Vuex中获取showAICall状态(虽然当前组件不使用,但保持连接)
|
||||
...mapState('app', ['showAICall'])
|
||||
},
|
||||
mounted() {
|
||||
this.getShowAiCase()
|
||||
},
|
||||
methods: {
|
||||
// 是否展示入口
|
||||
getShowAiCase() {
|
||||
showCaseAiEntrance().then(res => {
|
||||
this.showAiCase = res.result
|
||||
})
|
||||
},
|
||||
// 案例立即咨询
|
||||
getAICase() {
|
||||
// 通过Vuex控制AICall组件显示
|
||||
this.$store.dispatch('app/setShowAICall', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.AI-case {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
span {
|
||||
width: 160px;
|
||||
height: 40px;
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 105px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1
src/views/portal/case/components/map.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1759024984858" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4686" xmlns:xlink="http://www.w3.org/1999/xlink" width="25" height="25"><path d="M451.673935 994.395699C478.883834 1025.019147 524.254807 1024.808979 551.400292 993.928851 553.755808 991.387908 558.821323 985.796762 565.872444 977.84835 577.572838 964.659017 590.597131 949.62432 604.615947 932.998315 644.662065 885.504506 684.708678 834.717818 722.129538 782.646447 759.658524 730.424619 792.492213 679.709274 819.314991 631.458462 868.685946 542.646317 896 465.543426 896 402.285715 896 180.109449 719.301715 0 501.333333 0 283.364952 0 106.666667 180.109449 106.666667 402.285715 106.666667 465.598716 134.05152 542.80573 183.54613 631.762622 210.371803 679.976529 243.193308 730.651876 280.699364 782.833154 318.155192 834.94455 358.239268 885.77421 398.322835 933.311031 412.354743 949.952073 425.391185 965.00073 437.102468 978.202579 444.160087 986.158466 449.230214 991.754921 451.982775 994.736706L451.673935 994.395699ZM486.822684 961.321348C484.281231 958.568254 479.425084 953.207989 472.585916 945.498359 461.135889 932.591017 448.364015 917.847761 434.602351 901.527215 395.275714 854.888073 355.949587 805.019548 319.289224 754.014863 282.808749 703.260452 250.983685 654.123578 225.158316 607.707522 179.388826 525.445805 154.50505 455.290161 154.50505 402.285715 154.50505 207.039905 309.785362 48.761905 501.333333 48.761905 692.881306 48.761905 848.161617 207.039905 848.161617 402.285715 848.161617 455.246022 823.345286 525.298263 777.693969 607.419251 751.873483 653.867066 720.038415 703.039925 683.537446 753.831262 646.912604 804.794967 607.624538 854.619674 568.335977 901.215038 554.587654 917.520243 541.828177 932.24925 530.389289 945.143797 523.556841 952.845711 518.705521 958.200435 516.166694 960.950526 507.543772 970.748911 495.255793 970.80583 487.131524 961.662353L486.822684 961.321348Z" fill="#979797" p-id="4687"></path><path d="M714.955981 467.028806C723.919106 442.627955 728.565658 416.668998 728.565658 390.095238 728.565658 268.908183 632.184774 170.666667 513.29293 170.666667 394.401086 170.666667 298.020202 268.908183 298.020202 390.095238 298.020202 511.282291 394.401086 609.52381 513.29293 609.52381 549.003859 609.52381 583.510052 600.631947 614.373097 583.874409 626.032316 577.543868 630.449257 562.77782 624.238611 550.893519 618.027966 539.009218 603.541579 534.507006 591.882359 540.837549 567.900883 553.858639 541.111735 560.761905 513.29293 560.761905 420.821495 560.761905 345.858586 484.351836 345.858586 390.095238 345.858586 295.838641 420.821495 219.428572 513.29293 219.428572 605.764365 219.428572 680.727273 295.838641 680.727273 390.095238 680.727273 410.807981 677.117041 430.977316 670.154965 449.930592 665.522846 462.540883 671.796821 476.591108 684.168282 481.312651 696.53974 486.034191 710.323861 479.639095 714.955981 467.028806L714.955981 467.028806Z" fill="#979797" p-id="4688"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
423
src/views/portal/case/components/messages.vue
Normal file
@@ -0,0 +1,423 @@
|
||||
<template>
|
||||
<div class="messages">
|
||||
<!-- 机器人消息 -->
|
||||
<div v-if="messageData.isBot" class="bot-message">
|
||||
<!-- 思考中提示 -->
|
||||
<div v-if="messageData.thinkText" class="bot-think" v-katex:auto v-html="md.render(messageData.thinkText)"></div>
|
||||
|
||||
<!-- 主要回复内容 -->
|
||||
<div
|
||||
ref="contentContainer"
|
||||
class="message-content"
|
||||
v-katex:auto
|
||||
v-html="md.render(displayText)"
|
||||
></div>
|
||||
|
||||
<!-- 引用案例 -->
|
||||
<div v-if="messageData.caseRefers && messageData.caseRefers.length > 0 && messageData.textCompleted" class="case-refers">
|
||||
<div class="case-refers-title">
|
||||
<span><i class="iconfont icon-think"></i> 引用案例</span>
|
||||
<span v-if="shouldShowMoreButton" class="more" @click="toggleShowAllCaseRefers">
|
||||
{{ showAllCaseRefers ? '收起' : '查看更多' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="case-refers-list">
|
||||
<div
|
||||
v-for="item in displayedCaseRefers"
|
||||
:key="item.caseId"
|
||||
class="case-refers-item"
|
||||
>
|
||||
<div class="case-refers-item-title">
|
||||
<a @click="toUrl(item)" class="title">{{ item.title }}</a>
|
||||
<span class="case-refers-item-timer">{{ item.uploadTime }}</span>
|
||||
</div>
|
||||
<div class="case-refers-item-author">
|
||||
<span class="user"></span>
|
||||
<span>{{ item.authorName }}</span>
|
||||
<span class="case-inter-orginInfo">{{ item.orgInfo }}</span>
|
||||
</div>
|
||||
<div class="case-refers-item-keywords">
|
||||
<span v-for="keyword in item.keywords" :key="keyword">{{ keyword }}</span>
|
||||
</div>
|
||||
<div class="message-content case-content" v-html="md.render(item.content)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 用户消息 -->
|
||||
<div v-else class="user-message">
|
||||
<div class="message-text" v-html="messageData.text"></div>
|
||||
</div>
|
||||
|
||||
<!-- 推荐问题 -->
|
||||
<!-- <div v-if="suggestions && suggestions.length > 0" class="suggestions">-->
|
||||
<!-- <div class="suggestions-title">💡 推荐问题</div>-->
|
||||
<!-- <div class="suggestions-list">-->
|
||||
<!-- <button-->
|
||||
<!-- v-for="(item, index) in suggestions"-->
|
||||
<!-- :key="index"-->
|
||||
<!-- class="suggestions-item"-->
|
||||
<!-- @click="$emit('suggestion-click', item)"-->
|
||||
<!-- >-->
|
||||
<!-- {{ item }}-->
|
||||
<!-- </button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MarkdownIt from 'markdown-it';
|
||||
import highlight from 'markdown-it-highlightjs';
|
||||
import 'highlight.js/styles/a11y-dark.css';
|
||||
import markdownItMermaid from 'markdown-it-mermaid';
|
||||
import mermaid from 'mermaid';
|
||||
|
||||
// 初始化 Mermaid
|
||||
mermaid.initialize({ startOnLoad: false });
|
||||
|
||||
const md = new MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
});
|
||||
|
||||
md.use(highlight).use(markdownItMermaid);
|
||||
|
||||
export default {
|
||||
name: 'Message',
|
||||
props: {
|
||||
messageData: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
suggestions: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
md,
|
||||
displayText: '',
|
||||
typingTimer: null,
|
||||
typingSpeed: 30, // 毫秒/字符
|
||||
showAllCaseRefers: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
displayedCaseRefers() {
|
||||
if (this.showAllCaseRefers || !this.messageData.caseRefers) {
|
||||
return this.messageData.caseRefers || [];
|
||||
}
|
||||
return this.messageData.caseRefers.slice(0, 3);
|
||||
},
|
||||
shouldShowMoreButton() {
|
||||
return this.messageData.caseRefers && this.messageData.caseRefers.length > 3;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'messageData.text': {
|
||||
handler(newVal) {
|
||||
if (!newVal) {
|
||||
this.displayText = '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.messageData.isBot && !this.messageData.typing) {
|
||||
// this.startTyping(newVal); // 启动打字机效果/**/
|
||||
|
||||
this.displayText = newVal || ''
|
||||
} else {
|
||||
this.displayText = this.md.render(newVal);
|
||||
this.$nextTick(this.renderMermaid); // 直接渲染 Mermaid
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toUrl(item) {
|
||||
this.$router.push({
|
||||
path: '/case/detail',
|
||||
query: { id: item.caseId },
|
||||
});
|
||||
|
||||
|
||||
this.$emit('getMinWindow')
|
||||
},
|
||||
|
||||
// 正确的打字机效果:先整体渲染 Markdown,再逐字显示 HTML
|
||||
startTyping(fullText) {
|
||||
const renderedText = this.md.render(fullText);
|
||||
this.displayText = '';
|
||||
let index = 0;
|
||||
|
||||
if (this.typingTimer) {
|
||||
clearInterval(this.typingTimer);
|
||||
}
|
||||
|
||||
this.typingTimer = setInterval(() => {
|
||||
if (index < renderedText.length) {
|
||||
this.displayText += renderedText[index];
|
||||
index++;
|
||||
} else {
|
||||
clearInterval(this.typingTimer);
|
||||
this.typingTimer = null;
|
||||
this.$nextTick(this.renderMermaid); // 渲染 Mermaid 图表
|
||||
}
|
||||
}, this.typingSpeed);
|
||||
},
|
||||
|
||||
// 触发 Mermaid 渲染
|
||||
renderMermaid() {
|
||||
this.$nextTick(() => {
|
||||
const mermaidEls = this.$el.querySelectorAll('.mermaid');
|
||||
if (mermaidEls.length > 0) {
|
||||
try {
|
||||
// mermaid 8.x 版本使用 init 方法而不是 run
|
||||
if (typeof mermaid.init === 'function') {
|
||||
mermaid.init(undefined, '.mermaid');
|
||||
} else if (mermaid.default && typeof mermaid.default.init === 'function') {
|
||||
mermaid.default.init(undefined, '.mermaid');
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Mermaid 渲染失败:', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 切换案例引用显示数量
|
||||
toggleShowAllCaseRefers() {
|
||||
this.showAllCaseRefers = !this.showAllCaseRefers;
|
||||
// 切换后重新渲染 Mermaid(如果内容中有图表)
|
||||
this.$nextTick(this.renderMermaid);
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.typingTimer) {
|
||||
clearInterval(this.typingTimer);
|
||||
this.typingTimer = null;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
::v-deep .mermaid{
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
::v-deep svg[id^="mermaid-"]{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.messages {
|
||||
width: 100%;
|
||||
word-wrap: break-word;
|
||||
|
||||
.bot-message {
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.bot-think {
|
||||
color: #909399;
|
||||
padding-bottom: 5px;
|
||||
position: relative;
|
||||
padding-left: 10px;
|
||||
|
||||
&:before {
|
||||
content: ' ';
|
||||
border-left: 0.5px solid #909399;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: -3px;
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.message-content {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.case-content {
|
||||
font-size: 12px !important;
|
||||
margin-top: 8px;
|
||||
padding: 6px 10px;
|
||||
//background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
//border: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
|
||||
.user-message {
|
||||
float: right;
|
||||
padding: 8px 15px;
|
||||
max-width: 80%;
|
||||
background-color: #e4e7ed;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* ========== 案例引用样式 ========== */
|
||||
.case-refers {
|
||||
margin-top: 12px;
|
||||
|
||||
.case-refers-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #333;
|
||||
|
||||
.icon-think {
|
||||
background-image: url('./map.svg');
|
||||
width: 15px;
|
||||
height: 13px;
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.more {
|
||||
font-size: 12px;
|
||||
padding: 2px 8px;
|
||||
background-color: #f4f7fd;
|
||||
border-radius: 5px;
|
||||
color: #577ee1;
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.case-refers-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.case-refers-item {
|
||||
border: 1px solid rgba(144, 147, 153, 0.44);
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
|
||||
.case-refers-item-title {
|
||||
font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
.title {
|
||||
max-width: 70%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: #000;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.case-refers-item-timer {
|
||||
font-size: 11px;
|
||||
color: #aaa;
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.case-refers-item-author {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
margin-bottom: 5px;
|
||||
|
||||
.user {
|
||||
background-image: url('./user.svg');
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.case-inter-orginInfo {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.case-refers-item-keywords {
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
|
||||
span {
|
||||
padding: 2px 6px;
|
||||
background-color: #f4f7fd;
|
||||
border-radius: 5px;
|
||||
font-size: 11px !important;
|
||||
color: #577ee1;
|
||||
}
|
||||
|
||||
span + span {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 推荐问题 ========== */
|
||||
.suggestions {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
|
||||
.suggestions-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 6px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.suggestions-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.suggestions-item {
|
||||
padding: 6px 10px;
|
||||
background-color: #f0f4fc;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
text-align: left;
|
||||
color: #1a73e8;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:hover {
|
||||
background-color: #e1e8f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
src/views/portal/case/components/open.png
Normal file
|
After Width: | Height: | Size: 283 B |
391
src/views/portal/case/components/sendMessage.vue
Normal file
@@ -0,0 +1,391 @@
|
||||
<template>
|
||||
<div class="input-area">
|
||||
<el-input
|
||||
v-model="inputContent"
|
||||
type="textarea"
|
||||
class="input-placeholder"
|
||||
placeholder="有问题,尽管问"
|
||||
@keyup.enter.native="handleSend"
|
||||
:disabled="disabled"
|
||||
:autosize="{ minRows: 2, maxRows: 4}"
|
||||
resize="none"
|
||||
></el-input>
|
||||
<div class="action-buttons">
|
||||
<el-button type="primary" size="small" class="start-btn" @click="handleNewConversation">
|
||||
+ 开启新对话
|
||||
</el-button>
|
||||
<el-button type="text" class="send-btn" @click="handleSend" :disabled="disabled">
|
||||
<i class="el-icon-s-promotion"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { aiChat } from '@/api/boe/aiChat.js'
|
||||
|
||||
export default {
|
||||
name: 'SendMessage',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
messageList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
suggestions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
inputContent: this.value,
|
||||
conversationId: '' // 会话ID
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newVal) {
|
||||
this.inputContent = newVal
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSend() {
|
||||
if (!this.inputContent.trim() || this.disabled) return
|
||||
// 添加用户消息到列表
|
||||
const userMessage = {
|
||||
isBot: false,
|
||||
text: this.inputContent
|
||||
};
|
||||
this.messageList.push(userMessage);
|
||||
|
||||
// 显示加载状态
|
||||
this.$emit('loading', true);
|
||||
|
||||
// 调用AI聊天接口 (暂时注释掉SSE,使用模拟数据)
|
||||
this.callAIChat(this.inputContent);
|
||||
|
||||
// 清空输入框
|
||||
this.inputContent = ''
|
||||
},
|
||||
|
||||
|
||||
// 真实的SSE实现(暂时注释掉)
|
||||
callAIChat(question) {
|
||||
// 创建新的AI消息对象
|
||||
const aiMessage = {
|
||||
isBot: true,
|
||||
text: '',
|
||||
status:null,
|
||||
thinkText: '',
|
||||
caseRefers: [], // 添加caseRefers字段
|
||||
textCompleted: false // 添加文字处理完成状态,默认为false
|
||||
};
|
||||
this.messageList.push(aiMessage);
|
||||
|
||||
// 构造请求参数
|
||||
const requestData = {
|
||||
conversationId: this.conversationId,
|
||||
query: question
|
||||
};
|
||||
// 创建POST请求
|
||||
fetch('/systemapi/xboe/m/boe/case/ai/chat',{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
"accept": "text/event-stream",
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
}).then(r=>{
|
||||
return r
|
||||
}).then(response => {
|
||||
// 处理流式响应
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let buffer = '';
|
||||
let accumulatedContent = ''; // 累积的内容用于打字机效果
|
||||
let accumulatedThinkContent = ''; // 累积的思考内容
|
||||
let inThinkSection = false; // 是否在思考部分
|
||||
let typingTimer = null; // 打字机定时器
|
||||
let thinkTypingTimer = null; // 思考内容打字机定时器
|
||||
|
||||
// 逐字显示文本的函数
|
||||
const typeText = (message, fullContent) => {
|
||||
// 如果已有定时器在运行,先清除它
|
||||
if (typingTimer) {
|
||||
clearInterval(typingTimer);
|
||||
}
|
||||
|
||||
// 获取当前已显示的文本长度
|
||||
const currentLength = message.text.length;
|
||||
// 获取完整文本
|
||||
const targetLength = fullContent.length;
|
||||
|
||||
// 如果已经显示完整文本,不需要继续
|
||||
if (currentLength >= targetLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
const typingSpeed = 30; // 每个字符的间隔时间(毫秒)
|
||||
|
||||
typingTimer = setInterval(() => {
|
||||
// 计算下一个要显示的字符索引
|
||||
const nextIndex = message.text.length + 1;
|
||||
if (nextIndex <= targetLength) {
|
||||
message.text = fullContent.substring(0, nextIndex);
|
||||
this.$emit('update-message', message);
|
||||
} else {
|
||||
clearInterval(typingTimer);
|
||||
typingTimer = null;
|
||||
// 当打字机效果完成时,检查是否应该设置textCompleted为true
|
||||
// 这应该在status 4(交互完成)时才设置
|
||||
if (message.status === 4) {
|
||||
if (nextIndex >= targetLength) {
|
||||
message.textCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, typingSpeed);
|
||||
};
|
||||
|
||||
// 逐字显示思考内容的函数
|
||||
const typeThinkText = (message, fullThinkContent) => {
|
||||
// 如果已有定时器在运行,先清除它
|
||||
if (thinkTypingTimer) {
|
||||
clearInterval(thinkTypingTimer);
|
||||
}
|
||||
|
||||
// 获取当前已显示的文本长度
|
||||
const currentLength = message.thinkText.length;
|
||||
// 获取完整文本
|
||||
const targetLength = fullThinkContent.length;
|
||||
|
||||
// 如果已经显示完整文本,不需要继续
|
||||
if (currentLength >= targetLength) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从当前显示位置开始继续显示(避免清空重显)
|
||||
const startIndex = currentLength;
|
||||
|
||||
const typingSpeed = 20; // 每个字符的间隔时间(毫秒)
|
||||
|
||||
thinkTypingTimer = setInterval(() => {
|
||||
// 计算下一个要显示的字符索引
|
||||
const nextIndex = message.thinkText.length + 1;
|
||||
if (nextIndex <= targetLength) {
|
||||
message.thinkText = fullThinkContent.substring(0, nextIndex);
|
||||
this.$emit('update-message', message);
|
||||
} else {
|
||||
clearInterval(thinkTypingTimer);
|
||||
thinkTypingTimer = null;
|
||||
}
|
||||
}, typingSpeed);
|
||||
};
|
||||
|
||||
// 添加一个检查是否所有文本都已完成显示的函数
|
||||
const isTextDisplayCompleted = (message, fullContent) => {
|
||||
return message.text.length >= fullContent.length;
|
||||
};
|
||||
|
||||
// 读取流数据
|
||||
const read = () => {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
// 当流结束时,等待打字机效果完成
|
||||
const waitForTyping = () => {
|
||||
if (!typingTimer) {
|
||||
this.$emit('loading', false);
|
||||
} else {
|
||||
setTimeout(waitForTyping, 100);
|
||||
}
|
||||
};
|
||||
waitForTyping();
|
||||
return;
|
||||
}
|
||||
|
||||
// 解码数据
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
|
||||
// 按行分割数据
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop(); // 保留不完整的行
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data:')) {
|
||||
try {
|
||||
const jsonData = JSON.parse(line.substring(5));
|
||||
// 根据status处理不同类型的数据
|
||||
switch (jsonData.status) {
|
||||
case 0: // 引用文件
|
||||
// 处理引用文件信息
|
||||
if (jsonData.fileRefer && jsonData.fileRefer.caseRefers) {
|
||||
aiMessage.caseRefers = jsonData.fileRefer.caseRefers;
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
}
|
||||
// 从响应中获取并保存conversationId
|
||||
if (jsonData.conversationId) {
|
||||
this.conversationId = jsonData.conversationId;
|
||||
sessionStorage.setItem('conversationId', jsonData.conversationId);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1: // 流式对话内容
|
||||
// 处理
|
||||
const content = jsonData.content;
|
||||
aiMessage.hasThink = false;
|
||||
if (content.startsWith('<think>')) {
|
||||
aiMessage.hasThink = true
|
||||
inThinkSection = true;
|
||||
accumulatedThinkContent = content.replace('<think>', '');
|
||||
// 使用打字机效果显示think内容
|
||||
typeThinkText(aiMessage, accumulatedThinkContent);
|
||||
} else if (content.startsWith('</think>')) {
|
||||
inThinkSection = false;
|
||||
accumulatedThinkContent += content.replace('</think>', '');
|
||||
// 使用打字机效果显示think内容
|
||||
typeThinkText(aiMessage, accumulatedThinkContent);
|
||||
} else if (inThinkSection) {
|
||||
accumulatedThinkContent += content;
|
||||
// 使用打字机效果显示think内容
|
||||
typeThinkText(aiMessage, accumulatedThinkContent);
|
||||
} else {
|
||||
// 累积内容并使用打字机效果更新显示
|
||||
accumulatedContent += content;
|
||||
// 如果thinkText已经显示完整,则继续使用打字机效果显示内容
|
||||
if( aiMessage.hasThink){
|
||||
if(aiMessage.thinkText.length >=accumulatedThinkContent.length){
|
||||
typeText(aiMessage, accumulatedContent);
|
||||
}
|
||||
} else {
|
||||
typeText(aiMessage, accumulatedContent);
|
||||
}
|
||||
|
||||
}
|
||||
// 不在这里直接更新,让打字机效果处理更新
|
||||
break;
|
||||
|
||||
case 2: // 回答完成
|
||||
// 不再在这里设置textCompleted状态
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
// 从响应中获取并保存conversationId
|
||||
|
||||
break;
|
||||
|
||||
case 3: // 返回建议
|
||||
// 这里可以处理建议问题
|
||||
this.$emit('update-suggestions', jsonData.suggestions);
|
||||
break;
|
||||
|
||||
case 4: // 交互完成
|
||||
aiMessage.status = 4
|
||||
|
||||
// 从响应中获取并保存conversationId
|
||||
this.$emit('loading', false);
|
||||
// 检查文本是否已经完全显示,如果是则设置textCompleted为true
|
||||
if (isTextDisplayCompleted(aiMessage, accumulatedContent)) {
|
||||
// aiMessage.textCompleted = true;
|
||||
this.$emit('update-message', aiMessage);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析SSE数据错误:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 继续读取
|
||||
read();
|
||||
}).catch(error => {
|
||||
console.error('SSE连接错误:', error);
|
||||
// 出错时也设置文字处理完成状态
|
||||
if (typingTimer) {
|
||||
clearInterval(typingTimer);
|
||||
typingTimer = null;
|
||||
}
|
||||
aiMessage.textCompleted = true;
|
||||
this.$emit('loading', false);
|
||||
aiMessage.text = '当前无法获取回答,请稍后重试';
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
});
|
||||
};
|
||||
|
||||
// 开始读取数据
|
||||
read();
|
||||
}).catch(error => {
|
||||
console.error('请求失败:', error);
|
||||
// 出错时也设置文字处理完成状态
|
||||
aiMessage.textCompleted = true;
|
||||
this.$emit('loading', false);
|
||||
aiMessage.text = '当前无法获取回答,请稍后重试';
|
||||
// 更新父组件的messageList
|
||||
this.$emit('update-message', aiMessage);
|
||||
});
|
||||
},
|
||||
handleNewConversation() {
|
||||
this.conversationId = ''
|
||||
this.$emit('new-conversation')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.input-area {
|
||||
background-color: white;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
padding: 5px 16px 10px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.input-placeholder {
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
border: none;
|
||||
|
||||
::v-deep .el-input__inner {
|
||||
border: none;
|
||||
padding: 0;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 5px;
|
||||
|
||||
.start-btn {
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
color: #409eff;
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #dcdfe6;
|
||||
}
|
||||
|
||||
.send-btn {
|
||||
font-size: 18px;
|
||||
color: #409eff;
|
||||
padding: 6px;
|
||||
|
||||
&[disabled] {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
12
src/views/portal/case/components/u762.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="750px" height="850px" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<radialGradient cx="362.789473684209" cy="413.96491228069874" r="1153.015055438179" gradientTransform="matrix(0.023310357358899587 0.999728276703125 -0.9997282767031253 0.02331035735889959 768.1851497765263 41.62434690904212 )" gradientUnits="userSpaceOnUse" id="RadialGradient4">
|
||||
<stop id="Stop5" stop-color="#ffffff" offset="0" />
|
||||
<stop id="Stop6" stop-color="#d4def7" offset="1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="M 0 5.000000000000001 A 5 5 0 0 1 4.999999999999999 0 L 745 0 A 5 5 0 0 1 750 5 L 750 845 A 5 5 0 0 1 745 850 L 5 850 A 5 5 0 0 1 0 845 L 0 5 Z " fill-rule="nonzero" fill="url(#RadialGradient4)" stroke="none" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 858 B |
1
src/views/portal/case/components/user.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1759026139840" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5676" xmlns:xlink="http://www.w3.org/1999/xlink" width="25" height="25"><path d="M512 74.666667C270.933333 74.666667 74.666667 270.933333 74.666667 512S270.933333 949.333333 512 949.333333 949.333333 753.066667 949.333333 512 753.066667 74.666667 512 74.666667zM288 810.666667c0-123.733333 100.266667-224 224-224S736 686.933333 736 810.666667c-61.866667 46.933333-140.8 74.666667-224 74.666666s-162.133333-27.733333-224-74.666666z m128-384c0-53.333333 42.666667-96 96-96s96 42.666667 96 96-42.666667 96-96 96-96-42.666667-96-96z m377.6 328.533333c-19.2-96-85.333333-174.933333-174.933333-211.2 32-29.866667 51.2-70.4 51.2-117.333333 0-87.466667-72.533333-160-160-160s-160 72.533333-160 160c0 46.933333 19.2 87.466667 51.2 117.333333-89.6 36.266667-155.733333 115.2-174.933334 211.2-55.466667-66.133333-91.733333-149.333333-91.733333-243.2 0-204.8 168.533333-373.333333 373.333333-373.333333S885.333333 307.2 885.333333 512c0 93.866667-34.133333 177.066667-91.733333 243.2z" fill="#666666" p-id="5677"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,254 +1,186 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <div style="display:flex; padding-left:20px; width:100%">
|
||||
<!-- <div style="display:flex; padding-left:20px; width:100%">
|
||||
<div style="width:100px">完成情况</div>
|
||||
<div class="progress"><div class="msg">课程完成情况</div> <el-progress :text-inside="true" :stroke-width="16" :percentage="porcessData.course" status="warning" class="progressItem"> </el-progress> </div>
|
||||
<div class="progress" ><div class="msg">项目完成情况</div> <el-progress :text-inside="true" :stroke-width="16" :percentage="porcessData.project" status="success" class="progressItem"> </el-progress> </div>
|
||||
<div class="progress"><div class="msg">路径图完成情况</div> <el-progress :text-inside="true" :stroke-width="16" :percentage="porcessData.router" class="progressItem"> </el-progress> </div>
|
||||
|
||||
</div> -->
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 12px 32px 10px 22px;
|
||||
"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; padding: 12px 32px 10px 22px;">
|
||||
<div style="display: flex; justify-content: flex-start">
|
||||
<div>
|
||||
<el-select
|
||||
class="search-input"
|
||||
v-model="status"
|
||||
clearable
|
||||
placeholder="学习进度"
|
||||
>
|
||||
<el-select class="search-input" v-model="status" clearable placeholder="学习进度">
|
||||
<el-option label="全部" value=""></el-option>
|
||||
<el-option label="未开始" value="0"></el-option>
|
||||
<el-option label="进行中" value="1"></el-option>
|
||||
<el-option label="已完成" value="2"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div style="padding-left: 10px">
|
||||
<el-input
|
||||
v-model="params.cmtask_name"
|
||||
clearable
|
||||
placeholder="搜索名称"
|
||||
></el-input>
|
||||
</div>
|
||||
<div class="button-class" style="padding-left: 10px">
|
||||
<el-button type="primary" icon="el-icon-search" @click="search()"
|
||||
>搜索</el-button
|
||||
>
|
||||
</div>
|
||||
<div class="button-class" style="padding-left: 10px">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-refresh-right"
|
||||
@click="reset()"
|
||||
>重置</el-button
|
||||
>
|
||||
</div>
|
||||
<div style="padding-left: 10px"><el-input v-model="params.cmtask_name" clearable placeholder="搜索名称"></el-input></div>
|
||||
<div class="button-class" style="padding-left: 10px"><el-button type="primary" icon="el-icon-search" @click="search()">搜索</el-button></div>
|
||||
<div class="button-class" style="padding-left: 10px"><el-button type="primary" icon="el-icon-refresh-right" @click="reset()">重置</el-button></div>
|
||||
</div>
|
||||
</div>
|
||||
<!--课程列表内容-->
|
||||
<div class="uc-list" v-if="couresList.length > 0">
|
||||
<div
|
||||
class="uc-course"
|
||||
v-for="(item, idx) in couresList"
|
||||
@click="jumpRouter(item)"
|
||||
:key="idx"
|
||||
>
|
||||
<div class="uc-course" v-for="(item, idx) in couresList" @click="jumpRouter(item)" :key="idx">
|
||||
<div class="uc-course-img">
|
||||
<img :src="item.cmtask_imgurl" />
|
||||
<span class="spanname" v-if="item.cmtask_type == 1">学习路径图</span>
|
||||
<span class="spanname" v-if="item.cmtask_type == 2">学习项目</span>
|
||||
<span class="spanname" v-if="item.cmtask_type==1">学习路径图</span>
|
||||
<span class="spanname" v-if="item.cmtask_type==2">学习项目</span>
|
||||
<!-- 类型3不显示,20230307 东吴说已确认的修改 -->
|
||||
<!-- <span class="spanname" v-if="item.cmtask_type==3">老系统必修任务</span> -->
|
||||
<!-- <span class="spanname" v-if="item.cmtask_type==3">老系统必修任务</span> -->
|
||||
</div>
|
||||
<div class="uc-course-info">
|
||||
<!-- 任务名称 -->
|
||||
<div class="uc-course-name one-line-ellipsis" style="cursor: pointer">
|
||||
<span v-if="item.type == 10" class="uc-course-type2">录播</span>
|
||||
<span v-if="item.type == 20" class="uc-course-type2">录播</span>
|
||||
<span v-if="item.type==10" class="uc-course-type2">录播</span>
|
||||
<span v-if="item.type==20" class="uc-course-type2">录播</span>
|
||||
<!-- <a href="/course/detail?id=" target="_blank"> {{item.name}}</a> -->
|
||||
<span v-html="item.cmtask_name"></span>
|
||||
</div>
|
||||
<!-- <div class="uc-course-source" @click="jumpRouter(item)">来源:{{ item.cmtask_name }}</div> -->
|
||||
<div style="padding-top: 18px" class="uc-course-text">
|
||||
学习进度:
|
||||
<span v-if="item.cmtask_user_status == 0">未开始</span>
|
||||
<span v-if="item.cmtask_user_status == 1">进行中</span>
|
||||
<span v-if="item.cmtask_user_status == 2">已完成</span>
|
||||
<div style="padding-top: 18px; " class="uc-course-text">学习进度:
|
||||
<span v-if="item.cmtask_user_status==0">未开始</span>
|
||||
<span v-if="item.cmtask_user_status==1">进行中</span>
|
||||
<span v-if="item.cmtask_user_status==2">已完成</span>
|
||||
</div>
|
||||
<!-- <div class="uc-course-time">推送时间:{{ formatsec(Number(item.updated_at) * 1000 )}}</div> -->
|
||||
<div class="uc-course-time">推送时间:{{ item.created_at }}</div>
|
||||
<div class="uc-course-time">推送时间:{{ item.created_at}}</div>
|
||||
</div>
|
||||
<div class="uc-course-btns">
|
||||
<el-button @click="jumpRouter(item)" type="primary" size="small"
|
||||
>开始学习</el-button
|
||||
>
|
||||
<el-button @click="jumpRouter(item)" type="primary" size="small">开始学习</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="height: 150px; text-align: center; margin-top: 57px">
|
||||
<el-pagination
|
||||
background
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:current-page="page"
|
||||
:page-size="size"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
>
|
||||
<div style="height:150px; text-align: center; margin-top:57px;" >
|
||||
<el-pagination background
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:current-page="page"
|
||||
:page-size="size"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
<div v-else class="zan-wu">
|
||||
<div v-if="loading">
|
||||
<span v-if="couresList.length == 0">正在加载数据...</span>
|
||||
<span v-else>查询中...</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-if="couresList.length == 0">没有查询到相关内容</span>
|
||||
</div>
|
||||
<div v-if="loading">
|
||||
<span v-if="couresList.length==0">正在加载数据...</span>
|
||||
<span v-else>查询中...</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-if="couresList.length==0">没有查询到相关内容</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import studyItem from "@/components/Course/studyItem.vue";
|
||||
import apiManage from "@/api/manage/manage.js";
|
||||
import { getToken } from "@/utils/token";
|
||||
import { formatsec } from "@/utils/datetime.js";
|
||||
import axios from "axios";
|
||||
import studyItem from '@/components/Course/studyItem.vue';
|
||||
import apiManage from '@/api/manage/manage.js'
|
||||
import { getToken } from '@/utils/token'
|
||||
import {formatsec} from '@/utils/datetime.js'
|
||||
import axios from 'axios'
|
||||
export default {
|
||||
name: "ucStudyTask",
|
||||
name: 'ucStudyTask',
|
||||
components: { studyItem },
|
||||
data() {
|
||||
return {
|
||||
status: "",
|
||||
params: {
|
||||
cmtask_name: "",
|
||||
cmtask_status: "",
|
||||
type: "",
|
||||
status: '',
|
||||
params:{
|
||||
cmtask_name:'',
|
||||
cmtask_status:'',
|
||||
type:'',
|
||||
},
|
||||
porcessData: {
|
||||
course: 0,
|
||||
project: 0,
|
||||
router: 0,
|
||||
porcessData:{
|
||||
course:0,
|
||||
project:0,
|
||||
router:0,
|
||||
},
|
||||
loading: true,
|
||||
size: 10,
|
||||
page: 1,
|
||||
keyword: "",
|
||||
loading:true,
|
||||
size:10,
|
||||
page:1,
|
||||
keyword:'',
|
||||
formatsec,
|
||||
couresList: [],
|
||||
total: 0,
|
||||
couresList:[],
|
||||
total: 0
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadBoeData();
|
||||
this.getPossData();
|
||||
this.loadBoeData();
|
||||
this.getPossData()
|
||||
},
|
||||
methods: {
|
||||
jumpRouter(item) {
|
||||
if (item.szxygProjectFlag == 1) {
|
||||
this.$router.push("/new-employee/study");
|
||||
return;
|
||||
}
|
||||
//location.href=`${this.webBaseUrl}/course/boeframe?id=${item.cmtask_id}`;
|
||||
|
||||
//let urlPre=window.location.protocol+'//'+window.location.host;
|
||||
let studentPath = process.env.VUE_APP_STUDENT_PATH;
|
||||
if (item.cmtask_type == 1) {
|
||||
//学习路径图
|
||||
let params = encodeURIComponent("routerId=" + item.cmtask_id);
|
||||
//this.$router.push('/forward?to='+studentPath+'/pathdetails¶ms='+params);
|
||||
this.$router.push(
|
||||
"/forward?to=" + studentPath + "/pathchapterdetails¶ms=" + params
|
||||
);
|
||||
} else if (item.cmtask_type == 2) {
|
||||
//学习项目
|
||||
let params = encodeURIComponent("projectId=" + item.cmtask_id);
|
||||
this.$router.push(
|
||||
"/forward?to=" + studentPath + "/projectdetails¶ms=" + params
|
||||
);
|
||||
} else if (item.cmtask_type == 3) {
|
||||
let pushUrl = `/course/boeframe?id=${item.cmtask_id}&type=3`;
|
||||
this.$router.push(pushUrl);
|
||||
let studentPath=process.env.VUE_APP_STUDENT_PATH;
|
||||
if(item.cmtask_type==1){ //学习路径图
|
||||
let params=encodeURIComponent('routerId='+item.cmtask_id);
|
||||
//this.$router.push('/forward?to='+studentPath+'/pathdetails¶ms='+params);
|
||||
this.$router.push('/forward?to='+studentPath+'/pathchapterdetails¶ms='+params);
|
||||
}else if(item.cmtask_type==2){ //学习项目
|
||||
let params=encodeURIComponent('projectId='+item.cmtask_id);
|
||||
this.$router.push('/forward?to='+studentPath+'/projectdetails¶ms='+params);
|
||||
}else if(item.cmtask_type==3){
|
||||
let pushUrl=`/course/boeframe?id=${item.cmtask_id}&type=3`;
|
||||
this.$router.push(pushUrl);
|
||||
}
|
||||
},
|
||||
getPossData() {
|
||||
axios({
|
||||
getPossData(){
|
||||
axios({
|
||||
method: "POST",
|
||||
url: "/report/boeu/studyInfo",
|
||||
url: '/report/boeu/studyInfo',
|
||||
params: {},
|
||||
headers: { token: getToken() },
|
||||
headers:{token:getToken(),}
|
||||
}).then((res) => {
|
||||
console.log("111", res.data.result);
|
||||
console.log('111',res.data.result)
|
||||
// this.porcessData=response.data.data
|
||||
this.porcessData.course = Math.floor(
|
||||
res.data.result.course.total === 0
|
||||
? 0
|
||||
: (res.data.result.course.completion /
|
||||
res.data.result.course.total) *
|
||||
100
|
||||
);
|
||||
this.porcessData.project = Math.floor(
|
||||
res.data.result.project.total === 0
|
||||
? 0
|
||||
: (res.data.result.project.completion /
|
||||
res.data.result.project.total) *
|
||||
100
|
||||
);
|
||||
this.porcessData.router = Math.floor(
|
||||
res.data.result.router.total === 0
|
||||
? 0
|
||||
: (res.data.result.router.completion /
|
||||
res.data.result.router.total) *
|
||||
100
|
||||
);
|
||||
});
|
||||
this.porcessData.course=Math.floor(res.data.result.course.total === 0 ? 0: (res.data.result.course.completion/res.data.result.course.total)*100)
|
||||
this.porcessData.project=Math.floor(res.data.result.project.total === 0 ? 0: (res.data.result.project.completion/res.data.result.project.total)*100)
|
||||
this.porcessData.router=Math.floor(res.data.result.router.total === 0 ? 0: (res.data.result.router.completion/res.data.result.router.total)*100)
|
||||
})
|
||||
},
|
||||
loadBoeData() {
|
||||
if (this.status !== "5") {
|
||||
if(this.status !== '5') {
|
||||
this.params.cmtask_status = this.status;
|
||||
}
|
||||
let params = {
|
||||
pageNo: this.page,
|
||||
pageSize: this.size,
|
||||
cmtask_user_status: this.params.cmtask_status,
|
||||
cmtask_name: this.params.cmtask_name,
|
||||
};
|
||||
this.loading = true;
|
||||
apiManage
|
||||
.userTaskList(params)
|
||||
.then((res) => {
|
||||
if (res.code == 200) {
|
||||
this.total = parseInt(res.data.total);
|
||||
this.couresList = res.data.records;
|
||||
} else {
|
||||
this.$message.error("查询数据失败:" + res.msg);
|
||||
}
|
||||
this.loading = false;
|
||||
let params={
|
||||
pageNo:this.page,
|
||||
pageSize:this.size,
|
||||
cmtask_user_status:this.params.cmtask_status,
|
||||
cmtask_name:this.params.cmtask_name,
|
||||
}
|
||||
this.loading=true;
|
||||
apiManage.userTaskList(params).then(res=>{
|
||||
if(res.code==200){
|
||||
this.total =parseInt(res.data.total);
|
||||
this.couresList = res.data.records;
|
||||
}else{
|
||||
this.$message.error('查询数据失败:'+res.msg);
|
||||
}
|
||||
this.loading=false;
|
||||
}).catch(()=>{
|
||||
this.loading=false;
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
search() {
|
||||
this.page = 1;
|
||||
this.loadBoeData();
|
||||
search(){
|
||||
this.page=1;
|
||||
this.loadBoeData()
|
||||
},
|
||||
reset() {
|
||||
this.loading = false;
|
||||
(this.params.cmtask_name = ""),
|
||||
(this.params.type = ""),
|
||||
(this.params.cmtask_status = "");
|
||||
this.status = "";
|
||||
this.page = 1;
|
||||
this.loadBoeData();
|
||||
this.loading=false;
|
||||
this.params.cmtask_name = '',
|
||||
this.params.type = '',
|
||||
this.params.cmtask_status = ''
|
||||
this.status = '';
|
||||
this.page=1;
|
||||
this.loadBoeData();
|
||||
},
|
||||
handleSizeChange(value) {
|
||||
this.size = value;
|
||||
@@ -258,25 +190,25 @@ export default {
|
||||
handleCurrentChange(value) {
|
||||
this.page = value;
|
||||
this.loadBoeData();
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.progress {
|
||||
width: calc((100% - 100px) / 3);
|
||||
.progress{
|
||||
width:calc((100% - 100px) / 3);
|
||||
display: flex;
|
||||
.msg {
|
||||
width: 120px;
|
||||
.msg{
|
||||
width:120px;
|
||||
}
|
||||
.progressItem {
|
||||
.progressItem{
|
||||
margin-left: 20px;
|
||||
width: calc(100% - 160px);
|
||||
height: 10px;
|
||||
width:calc(100% - 160px);
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
.zan-wu {
|
||||
.zan-wu{
|
||||
text-align: center;
|
||||
font-size: 20px;
|
||||
margin-top: 70px;
|
||||
@@ -286,19 +218,19 @@ export default {
|
||||
display: -webkit-box;
|
||||
white-space: pre-wrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-overflow:ellipsis;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 1;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.button-class {
|
||||
.button-class{
|
||||
// height: 42px;
|
||||
}
|
||||
|
||||
.uc-list {
|
||||
.uc-list{
|
||||
padding-left: 22px;
|
||||
}
|
||||
.uc-course-time {
|
||||
.uc-course-time{
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -306,23 +238,23 @@ export default {
|
||||
margin-top: 10px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
.spanname {
|
||||
position: absolute;
|
||||
background-color: #6a99a4;
|
||||
color: #ffffff;
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
bottom: 0px;
|
||||
z-index: 100;
|
||||
left: 0px;
|
||||
}
|
||||
.spanname {
|
||||
position: absolute;
|
||||
background-color: #6a99a4;
|
||||
color:#ffffff;
|
||||
padding: 4px 10px;
|
||||
font-size: 12px;
|
||||
bottom: 0px;
|
||||
z-index: 100;
|
||||
left: 0px;
|
||||
}
|
||||
.uc-course {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
margin-right: 32px;
|
||||
padding: 20px 0px 15px 0px;
|
||||
padding: 20px 0px 15px 0px ;
|
||||
.uc-course-img {
|
||||
width: 212px;
|
||||
height: 119px;
|
||||
@@ -331,6 +263,7 @@ export default {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
.uc-course-info {
|
||||
// width: 50%;
|
||||
@@ -345,9 +278,10 @@ export default {
|
||||
}
|
||||
.uc-course-text {
|
||||
color: #444;
|
||||
margin-bottom: 3px;
|
||||
margin-bottom:3px;
|
||||
font-size: 16px;
|
||||
margin-top: 20px;
|
||||
|
||||
}
|
||||
}
|
||||
.uc-course-btns {
|
||||
|
||||
@@ -57,6 +57,13 @@ module.exports = {
|
||||
// set svg-sprite-loader
|
||||
config.plugins.delete('preload')
|
||||
config.plugins.delete('prefetch')
|
||||
// 添加对 mathxyjax3 的处理规则
|
||||
config.module
|
||||
.rule('mathxyjax3')
|
||||
.test(/node_modules[\/\\]mathxyjax3[\/\\].*\.js$/)
|
||||
.use('null-loader')
|
||||
.loader('null-loader')
|
||||
.end()
|
||||
config.module
|
||||
.rule('svg')
|
||||
.exclude.add(resolve('src/icons'))
|
||||
@@ -131,15 +138,13 @@ module.exports = {
|
||||
// changeOrigin: true,
|
||||
// },
|
||||
"/growth": {
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
// target: 'http://192.168.86.195:32002',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/manageApi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
pathRewrite: {
|
||||
@@ -149,8 +154,7 @@ module.exports = {
|
||||
'/systemapi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'http://127.0.0.1:9090',
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
logLevel: 'debug',
|
||||
secure: false,
|
||||
@@ -160,8 +164,7 @@ module.exports = {
|
||||
},
|
||||
'/uboeApi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
pathRewrite: {
|
||||
@@ -170,8 +173,7 @@ module.exports = {
|
||||
},
|
||||
'/userbasic': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
pathRewrite: {
|
||||
@@ -181,8 +183,7 @@ module.exports = {
|
||||
'/statApi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'http://127.0.0.1:9080',
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
logLevel: 'debug',
|
||||
secure: false,
|
||||
@@ -193,8 +194,7 @@ module.exports = {
|
||||
'/socialApi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'http://127.0.0.1:9081',
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
logLevel: 'debug',
|
||||
secure: false,
|
||||
@@ -204,8 +204,7 @@ module.exports = {
|
||||
},
|
||||
'/report': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
pathRewrite: {
|
||||
@@ -214,15 +213,13 @@ module.exports = {
|
||||
},
|
||||
'/infrasApi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
'/activityApi': {
|
||||
// 目标代理服务器地址
|
||||
// target: 'https://u-pre.boe.com',
|
||||
target: 'http://10.10.176.17:81',
|
||||
target: 'https://u-pre.boe.com',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
|
||||