Merge branch 'main' of http://sl.vrgon.com:3000/JiXinHui/YingXingAI
This commit is contained in:
commit
9eeb8eadac
|
@ -0,0 +1,2 @@
|
||||||
|
# 忽略 unpackage 文件夹
|
||||||
|
/unpackage/
|
151
App.vue
151
App.vue
|
@ -1,31 +1,29 @@
|
||||||
<script>
|
<script>
|
||||||
import router from "./static/common/js/router";
|
import router from "./static/common/js/router";
|
||||||
import config from "./static/common/js/config.js"
|
import config from "./static/common/js/config.js";
|
||||||
|
|
||||||
var jweixin = require("jweixin-module");
|
var jweixin = require("jweixin-module");
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
show: false,
|
show: false,
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
globalData: {},
|
globalData: {},
|
||||||
created() {
|
created() {},
|
||||||
|
|
||||||
},
|
|
||||||
onLaunch() {
|
onLaunch() {
|
||||||
if (typeof window.entryUrl === 'undefined' || window.entryUrl === '') {
|
if (typeof window.entryUrl === "undefined" || window.entryUrl === "") {
|
||||||
window.entryUrl = location.href.split('#')[0]
|
window.entryUrl = location.href.split("#")[0];
|
||||||
}
|
}
|
||||||
router.initApp(this);
|
router.initApp(this);
|
||||||
// 处理项目加载时白名单不生效异常问题
|
// 处理项目加载时白名单不生效异常问题
|
||||||
let isUrl = this._route.fullPath.split('?')[0]
|
let isUrl = this._route.fullPath.split("?")[0];
|
||||||
let notNeed = config.whiteList.includes(isUrl)
|
let notNeed = config.whiteList.includes(isUrl);
|
||||||
if (notNeed) {
|
if (notNeed) {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: this._route.fullPath,
|
url: this._route.fullPath,
|
||||||
});
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
var that = this;
|
var that = this;
|
||||||
uni.getSystemInfo({
|
uni.getSystemInfo({
|
||||||
|
@ -36,7 +34,18 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return
|
|
||||||
|
if (!that.vuex_token) {
|
||||||
|
this.$u.vuex("vuex_user", "");
|
||||||
|
this.$u.vuex("vuex_token", "");
|
||||||
|
uni.clearStorage();
|
||||||
|
uni.reLaunch({
|
||||||
|
url: "/pages/login/login/index",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
if (!that.vuex_user.isFill) {
|
if (!that.vuex_user.isFill) {
|
||||||
this.$u.vuex("vuex_msgList", "");
|
this.$u.vuex("vuex_msgList", "");
|
||||||
this.$u.vuex("vuex_user", "");
|
this.$u.vuex("vuex_user", "");
|
||||||
|
@ -51,10 +60,8 @@ export default {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: "/pages/login/roleSelection",
|
url: "/pages/login/roleSelection",
|
||||||
});
|
});
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
// onShow(){
|
// onShow(){
|
||||||
// console.log('onShow')
|
// console.log('onShow')
|
||||||
|
@ -64,12 +71,12 @@ export default {
|
||||||
var that = this;
|
var that = this;
|
||||||
if (this.isWechat()) {
|
if (this.isWechat()) {
|
||||||
const isiOS = !!navigator.userAgent.match(
|
const isiOS = !!navigator.userAgent.match(
|
||||||
/\(i[^;]+;( U;)? CPU.+Mac OS X/
|
/\(i[^;]+;( U;)? CPU.+Mac OS X/
|
||||||
); //ios终端
|
); //ios终端
|
||||||
// 进行签名的时候 Android 不用使用之前的链接, ios 需要
|
// 进行签名的时候 Android 不用使用之前的链接, ios 需要
|
||||||
const signLink = isiOS
|
const signLink = isiOS
|
||||||
? window.entryUrl
|
? window.entryUrl
|
||||||
: window.location.href.split("#")[0];
|
: window.location.href.split("#")[0];
|
||||||
//获取当前url然后传递给后台获取授权和签名信息,后台需要解码才能使用
|
//获取当前url然后传递给后台获取授权和签名信息,后台需要解码才能使用
|
||||||
// const url =encodeURIComponent(signLink);
|
// const url =encodeURIComponent(signLink);
|
||||||
const url = signLink;
|
const url = signLink;
|
||||||
|
@ -100,14 +107,14 @@ export default {
|
||||||
type: "gcj02", // 默认为wgs84的gps坐标,如果要返 回直接给openLocation用的火星坐标,可传入'gcj02'
|
type: "gcj02", // 默认为wgs84的gps坐标,如果要返 回直接给openLocation用的火星坐标,可传入'gcj02'
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
that.$u.vuex('vuex_userLocation', res)
|
that.$u.vuex("vuex_userLocation", res);
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
userId: that.vuex_user.id,
|
userId: that.vuex_user.id,
|
||||||
longitude: res.longitude + "",
|
longitude: res.longitude + "",
|
||||||
latitude: res.latitude + "",
|
latitude: res.latitude + "",
|
||||||
};
|
};
|
||||||
that.$u.api.upPosition(data)
|
that.$u.api.upPosition(data);
|
||||||
},
|
},
|
||||||
fail: function (res) {
|
fail: function (res) {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
@ -126,16 +133,15 @@ export default {
|
||||||
isHighAccuracy: true,
|
isHighAccuracy: true,
|
||||||
highAccuracyExpireTime: 3000,
|
highAccuracyExpireTime: 3000,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
|
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
|
||||||
this.$u.vuex('vuex_userLocation', res)
|
this.$u.vuex("vuex_userLocation", res);
|
||||||
var data = {
|
var data = {
|
||||||
userId: this.vuex_user.id,
|
userId: this.vuex_user.id,
|
||||||
longitude: res.longitude + "",
|
longitude: res.longitude + "",
|
||||||
latitude: res.latitude + "",
|
latitude: res.latitude + "",
|
||||||
};
|
};
|
||||||
this.$u.api.upPosition(data)
|
this.$u.api.upPosition(data);
|
||||||
},
|
},
|
||||||
fail: (err) => {
|
fail: (err) => {
|
||||||
uni.hideLoading();
|
uni.hideLoading();
|
||||||
|
@ -160,75 +166,76 @@ export default {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
that.$connection
|
that.$connection
|
||||||
.start()
|
.start()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// console.log(that.$connection.connection.transport.webSocket._webSocket.url)
|
// console.log(that.$connection.connection.transport.webSocket._webSocket.url)
|
||||||
// that.connectionId = that.$connection.connection.transport.url.split(
|
// that.connectionId = that.$connection.connection.transport.url.split(
|
||||||
// "=")[1]
|
// "=")[1]
|
||||||
that.connectionId = that.$connection.connection.transport.webSocket._webSocket.url.split(
|
that.connectionId =
|
||||||
"=")[1]
|
that.$connection.connection.transport.webSocket._webSocket.url.split(
|
||||||
that.CharLogin()
|
"="
|
||||||
})
|
)[1];
|
||||||
.catch((e) => {
|
that.CharLogin();
|
||||||
//重试
|
})
|
||||||
that.$connection.sto
|
.catch((e) => {
|
||||||
console.log("websocket连接失败", e);
|
//重试
|
||||||
return start(5000);
|
that.$connection.sto;
|
||||||
});
|
console.log("websocket连接失败", e);
|
||||||
|
return start(5000);
|
||||||
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
//重试
|
//重试
|
||||||
return start(5000);
|
return start(5000);
|
||||||
}
|
}
|
||||||
}, ms);
|
}, ms);
|
||||||
})(0)
|
})(0);
|
||||||
//接收消息
|
//接收消息
|
||||||
this.$connection.on("ReceiveMessage", (user, message, type) => {
|
this.$connection.on("ReceiveMessage", (user, message, type) => {
|
||||||
|
if (this._route.fullPath.indexOf("dialogBox") < 0) {
|
||||||
if (this._route.fullPath.indexOf('dialogBox') < 0) {
|
|
||||||
var tab = this.vuex_tabbar;
|
var tab = this.vuex_tabbar;
|
||||||
tab[1].isDot = true;
|
tab[1].isDot = true;
|
||||||
this.$u.vuex('vuex_tabbar', tab);
|
this.$u.vuex("vuex_tabbar", tab);
|
||||||
var msgList = this.vuex_tabbar
|
var msgList = this.vuex_tabbar;
|
||||||
var msgList = this.vuex_msgList
|
var msgList = this.vuex_msgList;
|
||||||
// 管理员对普通用户
|
// 管理员对普通用户
|
||||||
if (type == 4) {
|
if (type == 4) {
|
||||||
user = 'admin'
|
user = "admin";
|
||||||
}
|
}
|
||||||
if (type == 3) {
|
if (type == 3) {
|
||||||
user += 'user'
|
user += "user";
|
||||||
}
|
}
|
||||||
if (msgList.indexOf(user) < 0) {
|
if (msgList.indexOf(user) < 0) {
|
||||||
msgList += (user + ',');
|
msgList += user + ",";
|
||||||
}
|
}
|
||||||
this.$u.vuex('vuex_msgList', msgList)
|
this.$u.vuex("vuex_msgList", msgList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//接收系统消息
|
//接收系统消息
|
||||||
this.$connection.on("SystemMessage", (title, content, time) => {
|
this.$connection.on("SystemMessage", (title, content, time) => {
|
||||||
if (this._route.fullPath.indexOf('sysList') < 0) {
|
if (this._route.fullPath.indexOf("sysList") < 0) {
|
||||||
var tab = this.vuex_tabbar;
|
var tab = this.vuex_tabbar;
|
||||||
tab[1].isDot = true;
|
tab[1].isDot = true;
|
||||||
this.$u.vuex('vuex_tabbar', tab);
|
this.$u.vuex("vuex_tabbar", tab);
|
||||||
var msgList = this.vuex_tabbar
|
var msgList = this.vuex_tabbar;
|
||||||
var msgList = this.vuex_msgList
|
var msgList = this.vuex_msgList;
|
||||||
if (msgList.indexOf('SystemMessage') < 0) {
|
if (msgList.indexOf("SystemMessage") < 0) {
|
||||||
msgList += 'SystemMessage,';
|
msgList += "SystemMessage,";
|
||||||
}
|
}
|
||||||
this.$u.vuex('vuex_msgList', msgList)
|
this.$u.vuex("vuex_msgList", msgList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//接收互动消息
|
//接收互动消息
|
||||||
this.$connection.on("InteractMessage", (data, type) => {
|
this.$connection.on("InteractMessage", (data, type) => {
|
||||||
if (this._route.fullPath.indexOf('interactionList') < 0) {
|
if (this._route.fullPath.indexOf("interactionList") < 0) {
|
||||||
var tab = this.vuex_tabbar;
|
var tab = this.vuex_tabbar;
|
||||||
tab[1].isDot = true;
|
tab[1].isDot = true;
|
||||||
this.$u.vuex('vuex_tabbar', tab);
|
this.$u.vuex("vuex_tabbar", tab);
|
||||||
var msgList = this.vuex_tabbar
|
var msgList = this.vuex_tabbar;
|
||||||
var msgList = this.vuex_msgList
|
var msgList = this.vuex_msgList;
|
||||||
if (msgList.indexOf('InteractMessage') < 0) {
|
if (msgList.indexOf("InteractMessage") < 0) {
|
||||||
msgList += 'InteractMessage,';
|
msgList += "InteractMessage,";
|
||||||
}
|
}
|
||||||
this.$u.vuex('vuex_msgList', msgList)
|
this.$u.vuex("vuex_msgList", msgList);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -236,23 +243,20 @@ export default {
|
||||||
//绑定用户账号和connectionId
|
//绑定用户账号和connectionId
|
||||||
if (this.vuex_user.id) {
|
if (this.vuex_user.id) {
|
||||||
this.$connection
|
this.$connection
|
||||||
.invoke("login", this.connectionId, this.vuex_user.id)
|
.invoke("login", this.connectionId, this.vuex_user.id)
|
||||||
.then((res) => {
|
.then((res) => {})
|
||||||
|
.catch((err) => {});
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
});
|
|
||||||
//初始化获取定位
|
//初始化获取定位
|
||||||
// this.getLocation()
|
// this.getLocation()
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.CharLogin()
|
this.CharLogin();
|
||||||
}, 1000)
|
}, 1000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.charLink()
|
this.charLink();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -305,7 +309,7 @@ uni-modal .uni-modal__btn:after {
|
||||||
|
|
||||||
uni-modal .uni-modal__btn_default {
|
uni-modal .uni-modal__btn_default {
|
||||||
background: #e6f6ff;
|
background: #e6f6ff;
|
||||||
color: #3CB5FB !important;
|
color: #3cb5fb !important;
|
||||||
margin-right: 20rpx;
|
margin-right: 20rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +326,6 @@ uni-modal .uni-modal__btn_primary {
|
||||||
color: #ffffff !important;
|
color: #ffffff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uni-modal .uni-modal {
|
uni-modal .uni-modal {
|
||||||
padding: 40rpx;
|
padding: 40rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -357,8 +360,8 @@ uni-page-body {
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||||
"Microsoft Yahei", sans-serif;
|
"Microsoft Yahei", sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
// max-width: 1536rpx;
|
// max-width: 1536rpx;
|
||||||
|
|
|
@ -100,7 +100,11 @@ const install = (Vue, vm) => {
|
||||||
|
|
||||||
/** 首页ai对话 */
|
/** 首页ai对话 */
|
||||||
// 发送消息
|
// 发送消息
|
||||||
let SendMessageApi = (params = {}) => vm.$u.post('api/ChatAI/CreateChat', params);
|
let SendMessageApi = (params = {}) => vm.$u.post('api/ChatAI/CreateChat', params, {showLoading: false});
|
||||||
|
// 获取历史对话列表
|
||||||
|
let GetConversationPage = (params = {}) => vm.$u.get('api/ChatAI/GetConversationPage', params);
|
||||||
|
// 获取对话详情
|
||||||
|
let GetConversationDetail = (params = {}) => vm.$u.get('api/ChatAI/GetHistoricalConversations', params);
|
||||||
|
|
||||||
/** 登录 */
|
/** 登录 */
|
||||||
// 获取图形验证码
|
// 获取图形验证码
|
||||||
|
@ -157,7 +161,9 @@ const install = (Vue, vm) => {
|
||||||
SendMessageApi,
|
SendMessageApi,
|
||||||
GetCaptcha,
|
GetCaptcha,
|
||||||
GetStuVerifyCode,
|
GetStuVerifyCode,
|
||||||
StuLogin
|
StuLogin,
|
||||||
|
GetConversationPage,
|
||||||
|
GetConversationDetail
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const install = (Vue, vm) => {
|
||||||
// baseUrl: 'http://115.238.47.235:8993',
|
// baseUrl: 'http://115.238.47.235:8993',
|
||||||
// 如果将此值设置为true,拦截回调中将会返回服务端返回的所有数据response,而不是response.data
|
// 如果将此值设置为true,拦截回调中将会返回服务端返回的所有数据response,而不是response.data
|
||||||
// 设置为true后,就需要在this.$u.http.interceptor.response进行多一次的判断,请打印查看具体值
|
// 设置为true后,就需要在this.$u.http.interceptor.response进行多一次的判断,请打印查看具体值
|
||||||
// originalData: true,
|
originalData: true,
|
||||||
// 设置自定义头部content-type
|
// 设置自定义头部content-type
|
||||||
// header: {
|
// header: {
|
||||||
// 'content-type': 'xxx'
|
// 'content-type': 'xxx'
|
||||||
|
@ -31,9 +31,13 @@ const install = (Vue, vm) => {
|
||||||
// 方式四,如果token放在了Storage本地存储中,拦截是每次请求都执行的,所以哪怕您重新登录修改了Storage,下一次的请求将会是最新值
|
// 方式四,如果token放在了Storage本地存储中,拦截是每次请求都执行的,所以哪怕您重新登录修改了Storage,下一次的请求将会是最新值
|
||||||
// const token = uni.getStorageSync('token');
|
// const token = uni.getStorageSync('token');
|
||||||
// config.header.token = token;
|
// config.header.token = token;
|
||||||
uni.showLoading({
|
|
||||||
title: '加载中'
|
// 注释 7/15
|
||||||
});
|
// uni.showLoading({
|
||||||
|
// title: '请求中...'
|
||||||
|
// });
|
||||||
|
// 注释
|
||||||
|
|
||||||
// setTimeout(function () {
|
// setTimeout(function () {
|
||||||
// uni.hideLoading();
|
// uni.hideLoading();
|
||||||
// }, 2000);
|
// }, 2000);
|
||||||
|
@ -42,11 +46,20 @@ const install = (Vue, vm) => {
|
||||||
}
|
}
|
||||||
// 响应拦截,判断状态码是否通过
|
// 响应拦截,判断状态码是否通过
|
||||||
Vue.prototype.$u.http.interceptor.response = (res) => {
|
Vue.prototype.$u.http.interceptor.response = (res) => {
|
||||||
uni.hideLoading();
|
// uni.hideLoading();
|
||||||
|
|
||||||
|
// 检查是否为401未授权错误
|
||||||
|
if (res.statusCode === 401) {
|
||||||
|
handleAuthError(vm);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.data;
|
||||||
|
|
||||||
if (res.succeed == true || res.success == true) {
|
if (res.succeed == true || res.success == true) {
|
||||||
// return res.data || res;
|
// return res.data || res;
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
// uni.showToast({
|
// uni.showToast({
|
||||||
// title: res.error,
|
// title: res.error,
|
||||||
// duration: 2000,
|
// duration: 2000,
|
||||||
|
@ -55,13 +68,31 @@ const install = (Vue, vm) => {
|
||||||
// uni.navigateTo({
|
// uni.navigateTo({
|
||||||
// url: "/pages/login/login/login",
|
// url: "/pages/login/login/login",
|
||||||
// });
|
// });
|
||||||
return res;
|
|
||||||
// return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 提取处理401错误的函数
|
||||||
|
function handleAuthError(vm) {
|
||||||
|
vm.$u.vuex("vuex_user", "");
|
||||||
|
vm.$u.vuex("vuex_token", "");
|
||||||
|
uni.clearStorage();
|
||||||
|
|
||||||
|
// 显示提示
|
||||||
|
uni.showToast({
|
||||||
|
title: '登录已过期,请重新登录!',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 1500
|
||||||
|
});
|
||||||
|
|
||||||
|
// 延迟跳转到登录页
|
||||||
|
uni.reLaunch({
|
||||||
|
url: '/pages/login/login/index'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
install
|
install
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,8 +60,12 @@ export default {
|
||||||
.phone-popup {
|
.phone-popup {
|
||||||
width: 600rpx;
|
width: 600rpx;
|
||||||
padding: 60rpx 0;
|
padding: 60rpx 0;
|
||||||
background: #ffffff;
|
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
|
background-image: url("/static/common/images/images_bg_dialog.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 630rpx 100rpx;
|
||||||
|
background-position: -20rpx 0;
|
||||||
|
|
||||||
|
|
||||||
.phone-title {
|
.phone-title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
<template>
|
||||||
|
<u-popup v-model="showPopup" width="550rpx">
|
||||||
|
<view class="drawer-container">
|
||||||
|
<view class="drawer-header">
|
||||||
|
<text class="drawer-title" @click="handleCreateConversation">
|
||||||
|
<u-icon
|
||||||
|
class="drawer-title-icon"
|
||||||
|
name="plus"
|
||||||
|
size="32rpx"
|
||||||
|
color="#666666"
|
||||||
|
></u-icon>
|
||||||
|
新建对话
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="chat-history-list"
|
||||||
|
:scroll-into-view="scrollToView"
|
||||||
|
:show-scrollbar="false"
|
||||||
|
scroll-with-animation="true"
|
||||||
|
>
|
||||||
|
<!-- 使用新的数据结构渲染聊天历史 -->
|
||||||
|
<view
|
||||||
|
v-for="(group, groupIndex) in chatHistoryList3"
|
||||||
|
:key="'group-' + group.id + groupIndex"
|
||||||
|
>
|
||||||
|
<!-- 日期标题 -->
|
||||||
|
<view class="chat-day">
|
||||||
|
<text class="day-text">{{ group.id }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 该日期下的对话列表 -->
|
||||||
|
<view
|
||||||
|
class="chat-item"
|
||||||
|
v-for="(item, index) in group.conversation"
|
||||||
|
:key="'conv-' + groupIndex + '-' + index"
|
||||||
|
:id="'chat-item-' + item.id"
|
||||||
|
:class="{
|
||||||
|
'chat-item-active': item.isActiveChat,
|
||||||
|
}"
|
||||||
|
@click="selectChatItem(groupIndex, index, item.id)"
|
||||||
|
>
|
||||||
|
<!-- currentActiveGroup === groupIndex &&
|
||||||
|
currentActiveIndex === index, -->
|
||||||
|
<text class="chat-text">{{ item.title }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 添加底部空白区域 -->
|
||||||
|
<view class="bottom-space"></view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<view class="drawer-footer">
|
||||||
|
<view class="user-info">
|
||||||
|
<image
|
||||||
|
class="user-avatar"
|
||||||
|
src="/static/common/images/avatar.png"
|
||||||
|
></image>
|
||||||
|
<text class="user-name">{{ userName || "晓德塔," }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="settings">
|
||||||
|
<u-icon name="setting" size="40rpx" color="#999"></u-icon>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "ChatHistory",
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
chatHistoryList3: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
activeIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
userName: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPopup: false,
|
||||||
|
currentActiveGroup: -1,
|
||||||
|
currentActiveIndex: -1,
|
||||||
|
activeItemId: "", // 存储当前激活项的ID
|
||||||
|
scrollToView: "", // 用于scroll-into-view属性
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show: {
|
||||||
|
handler(newVal) {
|
||||||
|
this.showPopup = newVal;
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
showPopup(val) {
|
||||||
|
if (val !== this.show) {
|
||||||
|
this.$emit("update:show", val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 监听聊天历史数据变化,找到激活项并滚动
|
||||||
|
chatHistoryList3: {
|
||||||
|
handler() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollToActiveItem();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 滚动到激活的聊天项
|
||||||
|
scrollToActiveItem() {
|
||||||
|
// 查找激活的聊天项
|
||||||
|
let activeItemFound = false;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let groupIndex = 0;
|
||||||
|
groupIndex < this.chatHistoryList3.length;
|
||||||
|
groupIndex++
|
||||||
|
) {
|
||||||
|
const group = this.chatHistoryList3[groupIndex];
|
||||||
|
for (let index = 0; index < group.conversation.length; index++) {
|
||||||
|
const item = group.conversation[index];
|
||||||
|
if (item.isActiveChat) {
|
||||||
|
activeItemFound = true;
|
||||||
|
this.activeItemId = `chat-item-${item.id}`;
|
||||||
|
// 设置scrollToView的值,使scroll-view自动滚动到该元素
|
||||||
|
this.scrollToView = this.activeItemId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (activeItemFound) break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectChatItem(groupIndex, index, conversationId) {
|
||||||
|
// this.currentActiveGroup = groupIndex;
|
||||||
|
// this.currentActiveIndex = index;
|
||||||
|
|
||||||
|
// 向父组件发送选中的对话信息
|
||||||
|
this.$emit("select-conversation", {
|
||||||
|
conversationId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
handleCreateConversation() {
|
||||||
|
this.$emit("create-conversation");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.drawer-container {
|
||||||
|
padding: 32rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #ffffff;
|
||||||
|
|
||||||
|
.drawer-header {
|
||||||
|
padding: 0 32rpx;
|
||||||
|
height: 140rpx;
|
||||||
|
line-height: 120rpx;
|
||||||
|
|
||||||
|
.drawer-title {
|
||||||
|
font-family: DouyinSans;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #666666;
|
||||||
|
|
||||||
|
.drawer-title-icon {
|
||||||
|
margin-right: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-history-list {
|
||||||
|
flex: 1;
|
||||||
|
height: calc(100vh - 360rpx);
|
||||||
|
padding-bottom: 20rpx;
|
||||||
|
|
||||||
|
.chat-day {
|
||||||
|
display: flex;
|
||||||
|
margin: 20rpx 0 20rpx 30rpx;
|
||||||
|
|
||||||
|
.day-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999999;
|
||||||
|
font-family: PingFang SC;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-item {
|
||||||
|
padding: 24rpx 30rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.chat-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #303030;
|
||||||
|
font-family: PingFang SC;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-active {
|
||||||
|
background-color: #4f6aff;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(79, 106, 255, 0.3);
|
||||||
|
|
||||||
|
.chat-text {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-space {
|
||||||
|
height: 30rpx;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drawer-footer {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
height: 130rpx;
|
||||||
|
border: 1rpx solid #eeeeee;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.user-avatar {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: DouyinSans;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings {
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,114 @@
|
||||||
|
<template>
|
||||||
|
<!-- 完善信息 -->
|
||||||
|
<u-popup
|
||||||
|
v-model="showPopup"
|
||||||
|
mode="center"
|
||||||
|
border-radius="16"
|
||||||
|
closeable
|
||||||
|
close-icon-color="#999999"
|
||||||
|
close-icon-pos="top-right"
|
||||||
|
>
|
||||||
|
<view class="perfect-info-popup">
|
||||||
|
<view class="perfect-info-title">提示</view>
|
||||||
|
<view class="perfect-info-content">
|
||||||
|
<view class="perfect-info-content-text">请完善个人信息,便于老师更准确的回答</view>
|
||||||
|
</view>
|
||||||
|
<view class="perfect-info-button">
|
||||||
|
<u-button class="confirm-button" type="primary" @click="toPerfectInfo"
|
||||||
|
>去完善</u-button
|
||||||
|
>
|
||||||
|
<u-button class="cancel-button" type="default" @click="closePopup"
|
||||||
|
>下次再说</u-button
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "PerfectInfo",
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showPopup: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show: {
|
||||||
|
handler(newVal) {
|
||||||
|
this.showPopup = newVal;
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
showPopup(val) {
|
||||||
|
if (val !== this.show) {
|
||||||
|
this.$emit("update:show", val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
closePopup() {
|
||||||
|
this.showPopup = false;
|
||||||
|
this.$emit("update:show", false);
|
||||||
|
},
|
||||||
|
toPerfectInfo() {
|
||||||
|
this.showPopup = false;
|
||||||
|
this.$emit("update:show", false);
|
||||||
|
this.$router.push("/pages/home/userInfo/index");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.perfect-info-popup {
|
||||||
|
width: 600rpx;
|
||||||
|
padding: 60rpx 0;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
background-image: url("/static/common/images/images_bg_dialog.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 630rpx 100rpx;
|
||||||
|
background-position: -20rpx 0;
|
||||||
|
|
||||||
|
.perfect-info-title {
|
||||||
|
text-align: center;
|
||||||
|
font-family: DouyinSans;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 40rpx;
|
||||||
|
color: #477bfb;
|
||||||
|
margin-bottom: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.perfect-info-content {
|
||||||
|
margin-bottom: 100rpx;
|
||||||
|
text-align: center;
|
||||||
|
font-family: PingFang SC;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
|
||||||
|
&-text {
|
||||||
|
padding: 0 40rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.perfect-info-button {
|
||||||
|
padding: 0 40rpx;
|
||||||
|
|
||||||
|
.confirm-button {
|
||||||
|
background-color: #477BFB;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
border-radius: 16rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -9,9 +9,56 @@ import { marked } from 'marked';
|
||||||
import hljs from 'highlight.js';
|
import hljs from 'highlight.js';
|
||||||
import mpHtml from '@/node_modules/mp-html/dist/uni-app/components/mp-html/mp-html.vue';
|
import mpHtml from '@/node_modules/mp-html/dist/uni-app/components/mp-html/mp-html.vue';
|
||||||
|
|
||||||
|
// 自定义渲染器,修改列表的渲染方式
|
||||||
|
const renderer = new marked.Renderer();
|
||||||
|
|
||||||
|
// 重写粗体渲染方法,确保粗体文本不会导致不当换行
|
||||||
|
renderer.strong = function(text) {
|
||||||
|
// 确保文本周围没有不必要的空格
|
||||||
|
const trimmedText = text.trim();
|
||||||
|
return `<strong class="custom-strong" style="font-weight:900 !important;display:inline !important;white-space:normal !important;word-break:normal !important;">${trimmedText}</strong>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写列表项渲染方法 - 使用特殊的包装来确保内容在一行
|
||||||
|
renderer.listitem = function(text) {
|
||||||
|
return `<li class="custom-list-item"><div class="list-item-wrapper" style="text-align:left !important;letter-spacing:normal !important;word-spacing:normal !important;text-justify:none !important;">${text}</div></li>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写有序列表渲染方法
|
||||||
|
renderer.list = function(body, ordered) {
|
||||||
|
const type = ordered ? 'ol' : 'ul';
|
||||||
|
const className = ordered ? 'custom-ordered-list' : 'custom-unordered-list';
|
||||||
|
return `<${type} class="${className}">${body}</${type}>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写段落渲染方法,确保换行正确
|
||||||
|
renderer.paragraph = function(text) {
|
||||||
|
return `<p class="custom-paragraph">${text}</p>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写链接渲染方法,确保链接文本正常显示
|
||||||
|
renderer.link = function(href, title, text) {
|
||||||
|
return `<a href="${href}" title="${title || ''}" style="display:inline !important;white-space:normal !important;word-break:break-all !important;letter-spacing:normal !important;word-spacing:normal !important;text-align:left !important;">${text}</a>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写表格渲染方法,添加表格容器使其可滚动
|
||||||
|
renderer.table = function(header, body) {
|
||||||
|
return `<div class="table-container"><table class="custom-table">
|
||||||
|
<thead>${header}</thead>
|
||||||
|
<tbody>${body}</tbody>
|
||||||
|
</table></div>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 重写表格单元格渲染方法
|
||||||
|
renderer.tablecell = function(content, flags) {
|
||||||
|
const type = flags.header ? 'th' : 'td';
|
||||||
|
const align = flags.align ? ` style="text-align:${flags.align}"` : '';
|
||||||
|
return `<${type}${align}>${content}</${type}>`;
|
||||||
|
};
|
||||||
|
|
||||||
// 配置marked
|
// 配置marked
|
||||||
marked.use({
|
marked.use({
|
||||||
renderer: new marked.Renderer(),
|
renderer: renderer,
|
||||||
highlight: function(code, lang) {
|
highlight: function(code, lang) {
|
||||||
try {
|
try {
|
||||||
return hljs.highlightAuto(code, [lang]).value;
|
return hljs.highlightAuto(code, [lang]).value;
|
||||||
|
@ -54,15 +101,351 @@ export default {
|
||||||
renderedContent() {
|
renderedContent() {
|
||||||
if (!this.content) return '';
|
if (!this.content) return '';
|
||||||
try {
|
try {
|
||||||
// 添加自定义样式
|
// 添加自定义样式,确保内容自动换行和列表正确显示
|
||||||
const styleTag = this.customStyle ?
|
const styleTag = `<style>
|
||||||
`<style>${this.customStyle}</style>` : '';
|
* {
|
||||||
|
max-width: 100% !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 段落样式修复 */
|
||||||
|
.custom-paragraph, p {
|
||||||
|
display: block !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
margin: 10rpx 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
text-align: left !important; /* 改为左对齐,不要两端对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 解决粗体文本和普通文本换行问题 */
|
||||||
|
.custom-strong, strong, b {
|
||||||
|
display: inline !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
position: static !important;
|
||||||
|
float: none !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 专门用于列表项内容包装的容器 */
|
||||||
|
.list-item-wrapper {
|
||||||
|
display: inline-block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
/* 关键属性:防止内部元素分列 */
|
||||||
|
table-layout: fixed !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
text-align: left !important;
|
||||||
|
text-justify: none !important;
|
||||||
|
letter-spacing: normal !important;
|
||||||
|
word-spacing: normal !important;
|
||||||
|
text-align-last: left !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内联元素修复 */
|
||||||
|
span, a, em, strong, .custom-strong, b {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
float: none !important;
|
||||||
|
position: static !important;
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
/* 确保内部不会有奇怪的换行 */
|
||||||
|
word-break: keep-all !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 特别针对粗体文本 */
|
||||||
|
.custom-strong, strong, b {
|
||||||
|
font-weight: bold !important;
|
||||||
|
display: inline !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
position: static !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
float: none !important;
|
||||||
|
width: auto !important;
|
||||||
|
word-break: keep-all !important;
|
||||||
|
word-wrap: normal !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
color: inherit !important; /* 确保颜色正确继承 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 专门修复mp-html组件生成的列表结构 */
|
||||||
|
div[data-v-7e5387f] {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 处理列表编号所在的容器 - 重要修复 */
|
||||||
|
div[data-v-7e5387f] {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: nowrap !important; /* 防止编号和首个字符分开 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 首行缩进优化(确保列表项不会被意外截断) */
|
||||||
|
div div {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 有序列表样式 */
|
||||||
|
.custom-ordered-list, ol {
|
||||||
|
display: block !important;
|
||||||
|
counter-reset: item !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
margin: 10rpx 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
list-style-type: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无序列表样式 */
|
||||||
|
.custom-unordered-list, ul {
|
||||||
|
display: block !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
margin: 10rpx 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
list-style-type: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 针对mp-html生成的列表标记容器 */
|
||||||
|
div[style*="undefined"] {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对列表项第一个元素特殊处理 */
|
||||||
|
h4 {
|
||||||
|
display: inline !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项共用样式 */
|
||||||
|
.custom-list-item, ol li, ul li {
|
||||||
|
display: flex !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
margin-bottom: 8rpx !important;
|
||||||
|
width: 100% !important;
|
||||||
|
position: relative !important;
|
||||||
|
/* 确保内容在一行显示 */
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
|
align-items: flex-start !important; /* 顶部对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项内容样式 */
|
||||||
|
.custom-list-item > *:not(ol):not(ul),
|
||||||
|
ol li > *:not(ol):not(ul),
|
||||||
|
ul li > *:not(ol):not(ul) {
|
||||||
|
flex: 1 !important;
|
||||||
|
display: inline !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
width: auto !important;
|
||||||
|
/* 确保文本不会被分列 */
|
||||||
|
column-count: 1 !important;
|
||||||
|
column-width: auto !important;
|
||||||
|
column-span: all !important;
|
||||||
|
text-align: left !important; /* 确保文本左对齐 */
|
||||||
|
justify-content: flex-start !important; /* 左对齐 */
|
||||||
|
text-justify: none !important; /* 禁用两端对齐 */
|
||||||
|
letter-spacing: normal !important; /* 正常字间距 */
|
||||||
|
word-spacing: normal !important; /* 正常词间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项内的p标签特殊处理 */
|
||||||
|
ol li p, ul li p, .custom-list-item p {
|
||||||
|
display: inline !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
width: auto !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 有序列表项编号 */
|
||||||
|
.custom-ordered-list > .custom-list-item {
|
||||||
|
counter-increment: item !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-ordered-list > .custom-list-item::before,
|
||||||
|
ol > li::before {
|
||||||
|
content: counter(item)"." !important;
|
||||||
|
min-width: 40rpx !important;
|
||||||
|
width: 40rpx !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
text-align: right !important;
|
||||||
|
margin-right: 8rpx !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
align-self: flex-start !important; /* 顶部对齐 */
|
||||||
|
padding-top: 1rpx !important; /* 微调垂直位置 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无序列表项标记 */
|
||||||
|
.custom-unordered-list > .custom-list-item::before,
|
||||||
|
ul > li::before {
|
||||||
|
content: "-" !important;
|
||||||
|
min-width: 40rpx !important;
|
||||||
|
width: 40rpx !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
text-align: center !important;
|
||||||
|
margin-right: 8rpx !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
align-self: flex-start !important; /* 顶部对齐 */
|
||||||
|
padding-top: 1rpx !important; /* 微调垂直位置 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 代码块样式 */
|
||||||
|
pre, code {
|
||||||
|
white-space: pre-wrap !important;
|
||||||
|
word-wrap: break-word !important;
|
||||||
|
word-break: break-all !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用可能导致问题的CSS特性 */
|
||||||
|
* {
|
||||||
|
column-count: 1 !important;
|
||||||
|
column-gap: 0 !important;
|
||||||
|
column-rule: none !important;
|
||||||
|
column-span: all !important;
|
||||||
|
column-width: auto !important;
|
||||||
|
columns: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
${this.customStyle || ''}
|
||||||
|
</style>`;
|
||||||
|
|
||||||
// 渲染Markdown为HTML
|
// 对Markdown内容进行预处理,修复可能导致问题的模式
|
||||||
const htmlContent = marked(this.content);
|
let processedContent = this.content;
|
||||||
|
|
||||||
|
// 将Markdown内容分解为段落,分别处理每个段落
|
||||||
|
const paragraphs = processedContent.split('\n\n');
|
||||||
|
const processedParagraphs = paragraphs.map(paragraph => {
|
||||||
|
// 处理一个段落内的粗体文本
|
||||||
|
return paragraph.replace(/\*\*([^*]+?)\*\*/g, (match, content) => {
|
||||||
|
// 返回无缝拼接的HTML粗体标签
|
||||||
|
return `<strong style="font-weight:900 !important;display:inline !important;">${content}</strong>`;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 重新组合处理后的段落
|
||||||
|
processedContent = processedParagraphs.join('\n\n');
|
||||||
|
|
||||||
|
// 使用marked处理剩余的Markdown语法
|
||||||
|
let htmlContent = marked(processedContent);
|
||||||
|
|
||||||
|
// 后处理HTML,确保任何遗漏的粗体标记也能被处理
|
||||||
|
htmlContent = htmlContent
|
||||||
|
// 处理任何遗漏的**标记
|
||||||
|
.replace(/\*\*([^*]+?)\*\*/g, (match, content) => {
|
||||||
|
return `<strong style="font-weight:900 !important;display:inline !important;">${content}</strong>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理HTML,确保所有文本元素都有正确的显示属性
|
||||||
|
let processedHtml = htmlContent;
|
||||||
|
|
||||||
|
// 处理表格,确保表格正确显示
|
||||||
|
processedHtml = processedHtml.replace(/<table([^>]*)>/g, (match, attrs) => {
|
||||||
|
return `<div class="table-container"><table${attrs || ''} class="custom-table">`;
|
||||||
|
});
|
||||||
|
|
||||||
|
processedHtml = processedHtml.replace(/<\/table>/g, '</table></div>');
|
||||||
|
|
||||||
|
// 将所有段落包装在特殊div中,防止段落间的换行问题
|
||||||
|
processedHtml = processedHtml.replace(/<p([^>]*)>(.*?)<\/p>/gs, (match, attrs, content) => {
|
||||||
|
// 确保段落内容连续显示,不被换行打断
|
||||||
|
return `<p${attrs} style="white-space:normal !important;word-break:normal !important;overflow-wrap:break-word !important;">${content}</p>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 确保强制使用行内样式
|
||||||
|
processedHtml = processedHtml.replace(
|
||||||
|
/<strong/g,
|
||||||
|
'<strong style="font-weight:900 !important;display:inline !important;white-space:normal !important;word-break:normal !important;overflow-wrap:break-word !important;color:inherit !important;"'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 处理可能的HTML转义问题
|
||||||
|
processedHtml = processedHtml
|
||||||
|
.replace(/<strong/g, '<strong')
|
||||||
|
.replace(/<\/strong>/g, '</strong>');
|
||||||
|
|
||||||
|
// 添加强制粗体样式覆盖
|
||||||
|
processedHtml = `
|
||||||
|
<style>
|
||||||
|
strong, .custom-strong, b {
|
||||||
|
font-weight: 900 !important;
|
||||||
|
display: inline !important;
|
||||||
|
color: inherit !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
}
|
||||||
|
p, div, span {
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
text-align: left !important; /* 左对齐 */
|
||||||
|
text-justify: none !important; /* 禁用两端对齐 */
|
||||||
|
}
|
||||||
|
/* 关键样式:确保文本流不被打断 */
|
||||||
|
p strong, p b, strong, b {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
/* 针对段落内部元素的处理 */
|
||||||
|
p > * {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
/* 链接样式 */
|
||||||
|
a {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: break-all !important;
|
||||||
|
letter-spacing: normal !important; /* 正常字间距 */
|
||||||
|
word-spacing: normal !important; /* 正常词间距 */
|
||||||
|
text-align: left !important; /* 左对齐 */
|
||||||
|
}
|
||||||
|
/* 表格样式 */
|
||||||
|
table {
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 16rpx 0 !important;
|
||||||
|
table-layout: fixed !important;
|
||||||
|
border: 1px solid #e0e0e0 !important;
|
||||||
|
overflow-x: auto !important;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #e0e0e0 !important;
|
||||||
|
padding: 12rpx !important;
|
||||||
|
text-align: left !important;
|
||||||
|
word-break: break-word !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
font-size: 28rpx !important;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
}
|
||||||
|
/* 禁用所有可能导致文本被拉伸的样式 */
|
||||||
|
* {
|
||||||
|
text-align-last: left !important;
|
||||||
|
text-justify: none !important;
|
||||||
|
text-align: left !important;
|
||||||
|
letter-spacing: normal !important;
|
||||||
|
word-spacing: normal !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
` + processedHtml;
|
||||||
|
|
||||||
// 返回带样式的HTML
|
// 返回带样式的HTML
|
||||||
return styleTag + htmlContent;
|
return styleTag + processedHtml;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Markdown渲染错误:', error);
|
console.error('Markdown渲染错误:', error);
|
||||||
return `<p style="color: red;">Markdown渲染错误: ${error.message}</p>`;
|
return `<p style="color: red;">Markdown渲染错误: ${error.message}</p>`;
|
||||||
|
@ -77,5 +460,306 @@ export default {
|
||||||
|
|
||||||
.markdown-container {
|
.markdown-container {
|
||||||
padding: 20rpx;
|
padding: 20rpx;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全局样式 */
|
||||||
|
.markdown-container >>> * {
|
||||||
|
max-width: 100% !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 段落样式 */
|
||||||
|
.markdown-container >>> .custom-paragraph,
|
||||||
|
.markdown-container >>> p {
|
||||||
|
display: block !important;
|
||||||
|
/* margin: 10rpx 0 !important; */
|
||||||
|
width: 100% !important;
|
||||||
|
line-height: 1.6 !important;
|
||||||
|
text-align: left !important; /* 改为左对齐,不要两端对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 专门用于列表项内容包装的容器 */
|
||||||
|
.markdown-container >>> .list-item-wrapper {
|
||||||
|
display: inline-block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
/* 关键属性:防止内部元素分列 */
|
||||||
|
table-layout: fixed !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
text-align: left !important;
|
||||||
|
text-justify: none !important;
|
||||||
|
letter-spacing: normal !important;
|
||||||
|
word-spacing: normal !important;
|
||||||
|
text-align-last: left !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 内联元素修复 */
|
||||||
|
.markdown-container >>> span,
|
||||||
|
.markdown-container >>> a,
|
||||||
|
.markdown-container >>> em,
|
||||||
|
.markdown-container >>> strong,
|
||||||
|
.markdown-container >>> .custom-strong,
|
||||||
|
.markdown-container >>> b {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
float: none !important;
|
||||||
|
position: static !important;
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
/* 确保内部不会有奇怪的换行 */
|
||||||
|
/* 下面不注释的话会有奇怪的换行 */
|
||||||
|
/* word-break: keep-all !important; */
|
||||||
|
line-height: inherit !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 特别针对粗体文本 */
|
||||||
|
.markdown-container >>> .custom-strong,
|
||||||
|
.markdown-container >>> strong,
|
||||||
|
.markdown-container >>> b {
|
||||||
|
font-weight: 900 !important;
|
||||||
|
display: inline !important;
|
||||||
|
vertical-align: baseline !important;
|
||||||
|
position: static !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
border: none !important;
|
||||||
|
float: none !important;
|
||||||
|
width: auto !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
word-wrap: normal !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
color: inherit !important; /* 确保颜色正确继承 */
|
||||||
|
font-size: inherit !important; /* 确保字体大小正确继承 */
|
||||||
|
font-family: inherit !important; /* 确保字体系列正确继承 */
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 专门修复mp-html组件生成的列表结构 */
|
||||||
|
.markdown-container >>> div[data-v-7e5387f] {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 处理列表编号所在的容器 - 重要修复 */
|
||||||
|
.markdown-container >>> div[data-v-7e5387f] {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: nowrap !important; /* 防止编号和首个字符分开 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 首行缩进优化(确保列表项不会被意外截断) */
|
||||||
|
.markdown-container >>> div div {
|
||||||
|
display: inline !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 有序列表样式 */
|
||||||
|
.markdown-container >>> .custom-ordered-list,
|
||||||
|
.markdown-container >>> ol {
|
||||||
|
display: block !important;
|
||||||
|
counter-reset: item !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
margin: 10rpx 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
list-style-type: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无序列表样式 */
|
||||||
|
.markdown-container >>> .custom-unordered-list,
|
||||||
|
.markdown-container >>> ul {
|
||||||
|
display: block !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
margin: 10rpx 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
list-style-type: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 针对mp-html生成的列表标记容器 */
|
||||||
|
.markdown-container >>> div[style*="undefined"] {
|
||||||
|
display: inline !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 对列表项第一个元素特殊处理 */
|
||||||
|
.markdown-container >>> h4 {
|
||||||
|
display: inline !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项共用样式 */
|
||||||
|
.markdown-container >>> .custom-list-item,
|
||||||
|
.markdown-container >>> ol li,
|
||||||
|
.markdown-container >>> ul li {
|
||||||
|
display: flex !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
margin-bottom: 8rpx !important;
|
||||||
|
width: 100% !important;
|
||||||
|
position: relative !important;
|
||||||
|
/* 确保内容在一行显示 */
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
|
align-items: flex-start !important; /* 顶部对齐 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项内容样式 */
|
||||||
|
.markdown-container >>> .custom-list-item > *:not(ol):not(ul),
|
||||||
|
.markdown-container >>> ol li > *:not(ol):not(ul),
|
||||||
|
.markdown-container >>> ul li > *:not(ol):not(ul) {
|
||||||
|
flex: 1 !important;
|
||||||
|
display: inline !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
word-break: normal !important;
|
||||||
|
width: auto !important;
|
||||||
|
/* 确保文本不会被分列 */
|
||||||
|
column-count: 1 !important;
|
||||||
|
column-width: auto !important;
|
||||||
|
column-span: all !important;
|
||||||
|
text-align: left !important; /* 确保文本左对齐 */
|
||||||
|
justify-content: flex-start !important; /* 左对齐 */
|
||||||
|
text-justify: none !important; /* 禁用两端对齐 */
|
||||||
|
letter-spacing: normal !important; /* 正常字间距 */
|
||||||
|
word-spacing: normal !important; /* 正常词间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表项内的p标签特殊处理 */
|
||||||
|
.markdown-container >>> ol li p,
|
||||||
|
.markdown-container >>> ul li p,
|
||||||
|
.markdown-container >>> .custom-list-item p {
|
||||||
|
display: inline !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
width: auto !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 有序列表项编号 */
|
||||||
|
.markdown-container >>> .custom-ordered-list > .custom-list-item::before,
|
||||||
|
.markdown-container >>> ol > li::before {
|
||||||
|
counter-increment: item !important;
|
||||||
|
content: counter(item)"." !important;
|
||||||
|
min-width: 40rpx !important;
|
||||||
|
width: 40rpx !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
text-align: right !important;
|
||||||
|
margin-right: 8rpx !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
align-self: flex-start !important; /* 顶部对齐 */
|
||||||
|
/* padding-top: 1rpx !important; 微调垂直位置 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 无序列表项标记 */
|
||||||
|
.markdown-container >>> .custom-unordered-list > .custom-list-item::before,
|
||||||
|
.markdown-container >>> ul > li::before {
|
||||||
|
content: "-" !important;
|
||||||
|
min-width: 40rpx !important;
|
||||||
|
width: 40rpx !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
text-align: center !important;
|
||||||
|
margin-right: 8rpx !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
flex-shrink: 0 !important;
|
||||||
|
align-self: flex-start !important; /* 顶部对齐 */
|
||||||
|
/* padding-top: 1rpx !important; 微调垂直位置 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用可能导致问题的CSS特性 */
|
||||||
|
.markdown-container >>> * {
|
||||||
|
column-count: 1 !important;
|
||||||
|
column-gap: 0 !important;
|
||||||
|
column-rule: none !important;
|
||||||
|
column-span: all !important;
|
||||||
|
column-width: auto !important;
|
||||||
|
columns: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 代码块样式 */
|
||||||
|
.markdown-container >>> pre,
|
||||||
|
.markdown-container >>> code {
|
||||||
|
white-space: pre-wrap !important;
|
||||||
|
word-break: break-all !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格样式 */
|
||||||
|
.markdown-container >>> table {
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
table-layout: fixed !important; /* 使用固定表格布局 */
|
||||||
|
border: 1px solid #e0e0e0 !important;
|
||||||
|
font-size: 24rpx !important; /* 稍微减小字体大小,适应移动端 */
|
||||||
|
overflow-x: visible !important; /* 移除溢出隐藏 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-container >>> table th,
|
||||||
|
.markdown-container >>> table td {
|
||||||
|
border: 1px solid #e0e0e0 !important;
|
||||||
|
padding: 8rpx 10rpx !important; /* 减小内边距 */
|
||||||
|
text-align: left !important;
|
||||||
|
word-break: break-word !important; /* 确保长文本会自动换行 */
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: middle !important;
|
||||||
|
min-width: 80rpx !important; /* 设置最小宽度 */
|
||||||
|
max-width: none !important; /* 移除最大宽度限制 */
|
||||||
|
width: auto !important; /* 使列宽自动调整 */
|
||||||
|
overflow-wrap: break-word !important; /* 确保长单词会换行 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-container >>> table th {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
font-weight: bold !important;
|
||||||
|
color: #333333 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-container >>> table tr:nth-child(even) {
|
||||||
|
background-color: #fafafa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-container >>> table tr:hover {
|
||||||
|
background-color: #f0f0f0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格容器 - 移除水平滚动 */
|
||||||
|
.markdown-container >>> .table-container {
|
||||||
|
width: 100% !important;
|
||||||
|
overflow-x: visible !important; /* 移除水平滚动 */
|
||||||
|
margin: 16rpx 0 30rpx !important; /* 增加底部边距,与下方内容分开 */
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格整体显示 */
|
||||||
|
.markdown-container >>> .custom-table {
|
||||||
|
width: 100% !important;
|
||||||
|
table-layout: fixed !important; /* 固定布局 */
|
||||||
|
overflow: visible !important; /* 显示所有内容 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格标题 */
|
||||||
|
.markdown-container >>> table caption {
|
||||||
|
font-weight: bold !important;
|
||||||
|
padding: 10rpx !important;
|
||||||
|
caption-side: top !important;
|
||||||
|
text-align: center !important;
|
||||||
|
color: #333333 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分隔线样式优化 */
|
||||||
|
.markdown-container >>> hr {
|
||||||
|
display: block !important;
|
||||||
|
height: 2rpx !important;
|
||||||
|
border: 0 !important;
|
||||||
|
border-top: 2rpx solid #e0e0e0 !important;
|
||||||
|
margin: 24rpx 0 !important; /* 增加上下边距 */
|
||||||
|
padding: 0 !important;
|
||||||
|
clear: both !important;
|
||||||
|
background-color: #e0e0e0 !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -129,7 +129,7 @@
|
||||||
},
|
},
|
||||||
"h5" : {
|
"h5" : {
|
||||||
"devServer" : {
|
"devServer" : {
|
||||||
"port" : 8080,
|
"port" : 8088,
|
||||||
"disableHostCheck" : true,
|
"disableHostCheck" : true,
|
||||||
// "proxy" : {
|
// "proxy" : {
|
||||||
// "/api" : {
|
// "/api" : {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -21,7 +21,7 @@
|
||||||
<image
|
<image
|
||||||
class="avatar-image"
|
class="avatar-image"
|
||||||
:src="
|
:src="
|
||||||
userInfo.avatar || '/static/common/images/avatar_default.png'
|
userInfo.avatar || '/static/common/images/avatar_default.jpg'
|
||||||
"
|
"
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
@click="chooseAvatar"
|
@click="chooseAvatar"
|
||||||
|
@ -35,17 +35,22 @@
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<text class="item-label">姓名</text>
|
<text class="item-label">姓名</text>
|
||||||
<view class="item-content">
|
<view class="item-content">
|
||||||
<text>{{ userInfo.name }}</text>
|
<input
|
||||||
|
class="item-input"
|
||||||
|
v-model="userInfo.name"
|
||||||
|
placeholder="请输入姓名"
|
||||||
|
placeholder-style="color: #CCCCCC;"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<text class="item-label">性别</text>
|
<text class="item-label">性别</text>
|
||||||
<view class="item-content" @click="openPicker('gender')">
|
<view class="item-content" @click="openPicker('gender')">
|
||||||
<text class="placeholder-text" v-if="!userInfo.gender"
|
<text class="placeholder-text" v-if="!userInfo.genderText"
|
||||||
>请选择性别</text
|
>请选择性别</text
|
||||||
>
|
>
|
||||||
<text v-else>{{ userInfo.gender }}</text>
|
<text v-else>{{ userInfo.genderText }}</text>
|
||||||
<text class="arrow-icon">></text>
|
<text class="arrow-icon">></text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
@ -106,9 +111,26 @@
|
||||||
</u-button>
|
</u-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 性别选择器 -->
|
||||||
|
<u-select
|
||||||
|
v-model="showGenderSelect"
|
||||||
|
mode="single-column"
|
||||||
|
:list="genderList"
|
||||||
|
@confirm="confirmGender"
|
||||||
|
></u-select>
|
||||||
|
|
||||||
|
<!-- 归属地选择器 -->
|
||||||
|
<u-picker
|
||||||
|
v-model="showRegionSelect"
|
||||||
|
mode="single-column"
|
||||||
|
:list="regionList"
|
||||||
|
@confirm="confirmRegion"
|
||||||
|
></u-picker>
|
||||||
|
|
||||||
<!-- 选择器弹窗 -->
|
<!-- 选择器弹窗 -->
|
||||||
<u-picker
|
<u-picker
|
||||||
v-model="showPicker"
|
v-model="showPicker"
|
||||||
|
mode="selector"
|
||||||
:columns="pickerColumns"
|
:columns="pickerColumns"
|
||||||
@confirm="onPickerConfirm"
|
@confirm="onPickerConfirm"
|
||||||
@cancel="showPicker = false"
|
@cancel="showPicker = false"
|
||||||
|
@ -122,8 +144,8 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
userInfo: {
|
userInfo: {
|
||||||
avatar: "/static/common/images/avatar_default.png",
|
avatar: "",
|
||||||
name: "杨翼",
|
name: "",
|
||||||
gender: "",
|
gender: "",
|
||||||
region: "",
|
region: "",
|
||||||
examYear: "",
|
examYear: "",
|
||||||
|
@ -145,6 +167,33 @@ export default {
|
||||||
"机械工程",
|
"机械工程",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 性别选择器
|
||||||
|
showGenderSelect: false,
|
||||||
|
genderList: [
|
||||||
|
{
|
||||||
|
label: "男",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "女",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
// 归属地选择器
|
||||||
|
showRegionSelect: false,
|
||||||
|
regionList: [
|
||||||
|
{
|
||||||
|
label: "北京",
|
||||||
|
value: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "上海",
|
||||||
|
value: "1",
|
||||||
|
},
|
||||||
|
|
||||||
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {},
|
mounted() {},
|
||||||
|
@ -175,6 +224,11 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
openPicker(type) {
|
openPicker(type) {
|
||||||
|
if (type === "gender") {
|
||||||
|
this.showGenderSelect = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.currentPicker = type;
|
this.currentPicker = type;
|
||||||
this.pickerColumns = [this.pickerData[type]];
|
this.pickerColumns = [this.pickerData[type]];
|
||||||
this.showPicker = true;
|
this.showPicker = true;
|
||||||
|
@ -197,6 +251,14 @@ export default {
|
||||||
}
|
}
|
||||||
this.showPicker = false;
|
this.showPicker = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 确认性别
|
||||||
|
confirmGender(e) {
|
||||||
|
console.log("确认性别", e[0].value);
|
||||||
|
this.userInfo.gender = e[0].value;
|
||||||
|
this.userInfo.genderText = e[0].label;
|
||||||
|
this.showGenderSelect = false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -37,14 +37,14 @@
|
||||||
|
|
||||||
<view class="form-content">
|
<view class="form-content">
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="form-label"
|
<view class="form-label"
|
||||||
><image
|
><image
|
||||||
class="image_icon"
|
class="image_icon"
|
||||||
src="/static/common/images/icon_phoneNumber.png"
|
src="/static/common/images/icon_phoneNumber.png"
|
||||||
mode="scaleToFill"
|
mode="scaleToFill"
|
||||||
/>
|
/>
|
||||||
手机号</text
|
<text>手机号</text>
|
||||||
>
|
</view>
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
|
@ -59,13 +59,14 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-item" v-if="isTeacher && loginType === 'psd'">
|
<view class="form-item" v-if="isTeacher && loginType === 'psd'">
|
||||||
<text class="form-label"
|
<view class="form-label"
|
||||||
><image
|
><image
|
||||||
class="image_icon"
|
class="image_icon"
|
||||||
src="/static/common/images/icon_password.png"
|
src="/static/common/images/icon_password.png"
|
||||||
mode="scaleToFill"
|
mode="scaleToFill"
|
||||||
/>密码</text
|
/>
|
||||||
>
|
<text>密码</text>
|
||||||
|
</view>
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -81,14 +82,14 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-item" v-if="loginType === 'code'">
|
<view class="form-item" v-if="loginType === 'code'">
|
||||||
<text class="form-label">
|
<view class="form-label">
|
||||||
<image
|
<image
|
||||||
class="image_icon"
|
class="image_icon"
|
||||||
src="/static/common/images/icon_verificationCode.png"
|
src="/static/common/images/icon_verificationCode.png"
|
||||||
mode="scaleToFill"
|
mode="scaleToFill"
|
||||||
/>
|
/>
|
||||||
图形验证码</text
|
<text>图形验证码</text>
|
||||||
>
|
</view>
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -111,14 +112,14 @@
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="form-item" v-if="loginType === 'code'">
|
<view class="form-item" v-if="loginType === 'code'">
|
||||||
<text class="form-label">
|
<view class="form-label">
|
||||||
<image
|
<image
|
||||||
class="image_icon"
|
class="image_icon"
|
||||||
src="/static/common/images/icon_verificationCode.png"
|
src="/static/common/images/icon_verificationCode.png"
|
||||||
mode="scaleToFill"
|
mode="scaleToFill"
|
||||||
/>
|
/>
|
||||||
验证码</text
|
<text>验证码</text>
|
||||||
>
|
</view>
|
||||||
<view class="input-wrapper">
|
<view class="input-wrapper">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -236,6 +237,7 @@ export default {
|
||||||
title: res.error || "发送失败",
|
title: res.error || "发送失败",
|
||||||
type: "error",
|
type: "error",
|
||||||
});
|
});
|
||||||
|
this.refreshCaptcha();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -331,25 +333,27 @@ export default {
|
||||||
|
|
||||||
// 登录
|
// 登录
|
||||||
login() {
|
login() {
|
||||||
const res = {
|
// const res = {
|
||||||
data: {
|
// data: {
|
||||||
token:
|
// // token:
|
||||||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9zaWQiOiJiNTI2OWVkMS01YmM4LTExZjAtOWE4OS0wMDE2M2UyZDJkZDMiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsIlVzZXJJbmZvIjoie1wiSWRcIjpcImI1MjY5ZWQxLTViYzgtMTFmMC05YTg5LTAwMTYzZTJkMmRkM1wiLFwiTmFtZVwiOlwi6J-56ICB5biIXCIsXCJQaG9uZU51bWJlclwiOlwiMTM1ODg4ODg4ODhcIixcIlNleFwiOjEsXCJBY2NvdW50VHlwZVwiOjJ9IiwibmJmIjoxNzUyMjIxMzI0LCJleHAiOjE3NTIzMDc3MjR9.fFQPIfzbjsa3lBf37qGzSg9leHQVFwVyl0cwvwevhd8",
|
// // "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJBZG1pbiIsIlVzZXJJbmZvIjoie1wiSWRcIjpcImI1MjY5ZWQxLTViYzgtMTFmMC05YTg5LTAwMTYzZTJkMmRkM1wiLFwiTmFtZVwiOlwi6J-56ICB5biIXCIsXCJQaG9uZU51bWJlclwiOlwiMTM1ODg4ODg4ODhcIixcIlNleFwiOjEsXCJBY2NvdW50VHlwZVwiOjJ9IiwibmJmIjoxNzUyMTE4ODU0LCJleHAiOjE3NTIyMDUyNTR9.aF0Q-X3ebIld7RIKMMW68tXjmmR4OO08dXLAtHWuuwc",
|
||||||
},
|
// token:
|
||||||
};
|
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJVc2VyIiwiVXNlckluZm8iOiJ7XCJJZFwiOlwiNTljNzIxNmMtMTRhZS00NmRmLTkxYTItNGYzMjFlZjM2YjQ5XCIsXCJOYW1lXCI6XCIxNzMzNTM3NDg4N1wiLFwiUGhvbmVOdW1iZXJcIjpcIjE3MzM1Mzc0ODg3XCIsXCJTZXhcIjoyLFwiQWNjb3VudFR5cGVcIjowfSIsIm5iZiI6MTc1MjExMDcwOSwiZXhwIjoxNzUyMTk3MTA5fQ.VDv2KjGq3s3BbZxZjB3P-BaeDU_1vIdqFEWGGvdpIxU",
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
const token = res.data.token;
|
// const token = res.data.token;
|
||||||
// 解析获取用户信息
|
// // 解析获取用户信息
|
||||||
const userInfo = getUserInfoFromJWT(token);
|
// const userInfo = getUserInfoFromJWT(token);
|
||||||
// 保存登录后得到的用户数据
|
// // 保存登录后得到的用户数据
|
||||||
this.$u.vuex("vuex_token", token);
|
// this.$u.vuex("vuex_token", token);
|
||||||
this.$u.vuex("vuex_user", userInfo);
|
// this.$u.vuex("vuex_user", userInfo);
|
||||||
|
|
||||||
// 跳转至首页
|
// // 跳转至首页
|
||||||
uni.reLaunch({
|
// uni.reLaunch({
|
||||||
url: "/pages/home/index/index",
|
// url: "/pages/home/index/index",
|
||||||
});
|
// });
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
// 校验手机号
|
// 校验手机号
|
||||||
if (!this.validatePhone()) {
|
if (!this.validatePhone()) {
|
||||||
|
@ -454,7 +458,7 @@ export default {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
background-image: url(/static/common/images/images_bg.png);
|
background-image: url("@/static/common/images/images_bg.png");
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
|
@ -553,9 +557,8 @@ export default {
|
||||||
margin-bottom: 32rpx;
|
margin-bottom: 32rpx;
|
||||||
|
|
||||||
.form-label {
|
.form-label {
|
||||||
display: block;
|
display: flex;
|
||||||
position: relative;
|
align-items: center;
|
||||||
// padding-left: 30rpx;
|
|
||||||
font-family: PingFang SC;
|
font-family: PingFang SC;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #333333;
|
color: #333333;
|
||||||
|
@ -565,7 +568,6 @@ export default {
|
||||||
.image_icon {
|
.image_icon {
|
||||||
width: 28rpx;
|
width: 28rpx;
|
||||||
height: 28rpx;
|
height: 28rpx;
|
||||||
vertical-align: text-top;
|
|
||||||
margin-right: 16rpx;
|
margin-right: 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.9 KiB |
|
@ -1,6 +1,7 @@
|
||||||
export default {
|
export default {
|
||||||
//不需要登录的页面,白名单
|
//不需要登录的页面,白名单
|
||||||
whiteList: [
|
whiteList: [
|
||||||
|
"/pages/login/login/index",
|
||||||
"/pages/login/login/login",
|
"/pages/login/login/login",
|
||||||
"/pages/login/register/register",
|
"/pages/login/register/register",
|
||||||
"/pages/login/confirmPwd/confirmPwd",
|
"/pages/login/confirmPwd/confirmPwd",
|
||||||
|
@ -14,5 +15,5 @@ export default {
|
||||||
'/pages/login/recognitionResult/recognitionFailed'
|
'/pages/login/recognitionResult/recognitionFailed'
|
||||||
],
|
],
|
||||||
//登录页
|
//登录页
|
||||||
loginPage:"/pages/login/login/login"
|
loginPage:"/pages/login/login/index"
|
||||||
}
|
}
|
|
@ -1,56 +1,58 @@
|
||||||
import config from "./config.js"
|
import config from "./config.js";
|
||||||
const initApp = function(vm) {
|
const initApp = function (vm) {
|
||||||
/**
|
/**
|
||||||
* 页面跳转拦截器
|
* 页面跳转拦截器
|
||||||
*/
|
*/
|
||||||
let that = vm;
|
let that = vm;
|
||||||
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
|
let list = ["navigateTo", "redirectTo", "reLaunch", "switchTab"];
|
||||||
list.forEach(item => { //用遍历的方式分别为,uni.navigateTo,uni.redirectTo,uni.reLaunch,uni.switchTab这4个路由方法添加拦截器
|
list.forEach((item) => {
|
||||||
uni.addInterceptor(item, {
|
//用遍历的方式分别为,uni.navigateTo,uni.redirectTo,uni.reLaunch,uni.switchTab这4个路由方法添加拦截器
|
||||||
invoke(e) { // 调用前拦截
|
uni.addInterceptor(item, {
|
||||||
//获取用户的token
|
invoke(e) {
|
||||||
// console.log(e)
|
// 调用前拦截
|
||||||
const token = that.vuex_token,
|
//获取用户的token
|
||||||
//获取要跳转的页面路径(url去掉"?"和"?"后的参数)
|
// console.log(e)
|
||||||
url = e.url.split('?')[0];
|
const token = that.vuex_token,
|
||||||
let notNeed = config.whiteList.includes(url)
|
//获取要跳转的页面路径(url去掉"?"和"?"后的参数)
|
||||||
// 如果在whiteList里面就不需要登录
|
url = e.url.split("?")[0];
|
||||||
// console.log(notNeed)
|
let notNeed = config.whiteList.includes(url);
|
||||||
|
// 如果在whiteList里面就不需要登录
|
||||||
|
// console.log(notNeed)
|
||||||
|
|
||||||
if (notNeed) {
|
if (notNeed) {
|
||||||
return e
|
return e;
|
||||||
} else {
|
} else {
|
||||||
//需要登录
|
//需要登录
|
||||||
if (token == '') {
|
if (token == "") {
|
||||||
// uni.showToast({
|
// uni.showToast({
|
||||||
// title: '请先登录',
|
// title: '请先登录',
|
||||||
// icon: 'none'
|
// icon: 'none'
|
||||||
// })
|
// })
|
||||||
// uni.navigateTo({
|
// uni.navigateTo({
|
||||||
// url: config.loginPage
|
// url: config.loginPage
|
||||||
// })
|
// })
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/'
|
url: "/pages/login/login/index",
|
||||||
})
|
});
|
||||||
return false
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return e
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
},
|
fail(err) {
|
||||||
fail(err) { // 失败回调拦截
|
// 失败回调拦截
|
||||||
console.log(err)
|
console.log(err);
|
||||||
// if (Debug) {
|
// if (Debug) {
|
||||||
// uni.showModal({
|
// uni.showModal({
|
||||||
// content: JSON.stringify(err),
|
// content: JSON.stringify(err),
|
||||||
// showCancel: false
|
// showCancel: false
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
export default {
|
export default {
|
||||||
initApp: initApp
|
initApp: initApp,
|
||||||
}
|
};
|
||||||
|
|
|
@ -13,7 +13,8 @@ try {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
|
// 需要永久存储,且下次APP启动需要取出的,在state中的变量名
|
||||||
let saveStateKeys = ['vuex_user', 'vuex_token', 'vuex_msgList', 'vuex_glyType', 'vuex_userLocation','vuex_userInfo','vuex_user_hobby','vuex_teacherInfo'];
|
// let saveStateKeys = ['vuex_user', 'vuex_token', 'vuex_msgList', 'vuex_glyType', 'vuex_userLocation','vuex_userInfo','vuex_user_hobby'];
|
||||||
|
let saveStateKeys = ['vuex_user', 'vuex_token'];
|
||||||
|
|
||||||
// 保存变量到本地存储中
|
// 保存变量到本地存储中
|
||||||
const saveLifeData = function (key, value) {
|
const saveLifeData = function (key, value) {
|
||||||
|
|
|
@ -26,6 +26,9 @@ class Request {
|
||||||
options.header = Object.assign({}, this.config.header, options.header);
|
options.header = Object.assign({}, this.config.header, options.header);
|
||||||
options.method = options.method || this.config.method;
|
options.method = options.method || this.config.method;
|
||||||
|
|
||||||
|
// 从请求选项中获取showLoading参数,如果未定义,则使用全局配置
|
||||||
|
const showLoading = options.header.showLoading !== undefined ? options.header.showLoading : this.config.showLoading;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
options.complete = (response) => {
|
options.complete = (response) => {
|
||||||
// 请求返回后,隐藏loading(如果请求返回快的话,可能会没有loading)
|
// 请求返回后,隐藏loading(如果请求返回快的话,可能会没有loading)
|
||||||
|
@ -81,7 +84,7 @@ class Request {
|
||||||
// 是否显示loading
|
// 是否显示loading
|
||||||
// 加一个是否已有timer定时器的判断,否则有两个同时请求的时候,后者会清除前者的定时器id
|
// 加一个是否已有timer定时器的判断,否则有两个同时请求的时候,后者会清除前者的定时器id
|
||||||
// 而没有清除前者的定时器,导致前者超时,一直显示loading
|
// 而没有清除前者的定时器,导致前者超时,一直显示loading
|
||||||
if(this.config.showLoading && !this.config.timer) {
|
if(showLoading && !this.config.timer) {
|
||||||
this.config.timer = setTimeout(() => {
|
this.config.timer = setTimeout(() => {
|
||||||
uni.showLoading({
|
uni.showLoading({
|
||||||
title: this.config.loadingText,
|
title: this.config.loadingText,
|
||||||
|
|
|
@ -4,8 +4,8 @@ module.exports = {
|
||||||
devServer: {
|
devServer: {
|
||||||
// 调试时允许内网穿透,让外网的人访问到本地调试的H5页面
|
// 调试时允许内网穿透,让外网的人访问到本地调试的H5页面
|
||||||
disableHostCheck: true,
|
disableHostCheck: true,
|
||||||
port: '8080',
|
port: '8088',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//productionSourceMap: false,
|
//productionSourceMap: false,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue