-
+
+
-
+ {{ job.jobTitle }}
+
- {{ job.jobTitle }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+ {{ job.jobLocation }}
+
+ -->
-
+
- {{ job.postingDate || '发布日期' }}
+ 发布日期:{{ job.postingDate || '暂无数据' }}
+
- {{ vacanciesTo(job.vacancies) }}
+ 招聘人数:{{ vacanciesTo(job.vacancies) }}
@@ -338,6 +360,24 @@
{{ job.companyName }}
+
+ 地区:{{ job.regionName }}
+
+
+
+
+ 未通过审核
+
+
+
+
+
+
+
+
+ {{ Number(job.jobStatus) === 0 ? '已上架' : '已下架' }}
+
+
-
-
+
- {{ job.postingDate || '发布日期' }}
+ 发布日期:{{ job.postingDate || '暂无数据' }}
@@ -415,6 +462,24 @@
{{ job.companyName }}
+
+ 地区:{{ job.regionName }}
+
+
+
+
+ 未通过审核
+
+
+
+
+
+
+
+
+ {{ Number(job.jobStatus) === 0 ? '已上架' : '已下架' }}
+
+
+
+ showNewFilter = value"
+ />
+
-
-
+
+
+
+ ‹
+ 喀什智慧就业平台
+
+
+
+
@@ -26,7 +36,13 @@ const userStore = useUserStore();
onLoad((options) => {
// useReadMsg().fetchMessages();
});
-
+// 返回按钮功能
+function back() {
+ // uni.navigateBack({
+ // delta: 1
+ // });
+ window.location.href = 'https://www.xjksly.cn/mechine-single-vue/';
+}
onShow(() => {
// 更新自定义tabbar选中状态
tabbarManager.updateSelected(0);
@@ -191,4 +207,39 @@ onShow(() => {
background: #FFFFFF
width: 4rpx
height: 20rpx
+/* 自定义导航栏样式 */
+.custom-nav
+ background: linear-gradient(135deg, #8a9bf0, #c3cafa, #e7ebfe)
+ width: 100%
+ box-shadow: 0 2rpx 10rpx rgba(138, 155, 240, 0.2)
+ border-radius: 0
+ .nav-content
+ height: 80rpx
+ display: flex
+ align-items: center
+ justify-content: space-between
+ padding: 0 40rpx
+ .nav-back
+ width: 120rpx
+ height: 80rpx
+ display: flex
+ align-items: center
+ justify-content: flex-start
+ color: #2f56e8
+ font-weight: 400
+ transition: all 0.3s ease
+ &:hover
+ transform: translateX(-5rpx)
+ .nav-back-text
+ font-size: 56rpx
+ line-height: 1
+ text-shadow: 1rpx 1rpx 2rpx rgba(0, 0, 0, 0.1)
+ .nav-title
+ color: #4a55b0
+ font-size: 36rpx
+ font-weight: 600
+ text-shadow: 1rpx 1rpx 2rpx rgba(255, 255, 255, 0.8)
+ letter-spacing: 2rpx
+ .nav-placeholder
+ width: 120rpx
diff --git a/pages/login/sms-verify.vue b/pages/login/sms-verify.vue
new file mode 100644
index 0000000..5ce1577
--- /dev/null
+++ b/pages/login/sms-verify.vue
@@ -0,0 +1,634 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 为了您的账户安全需要您进行验证
+
+
+
+
+ 验证码将发送至
+ {{ formattedPhone }}
+
+
+
+
+ 请输入6位数字验证码
+
+
+
+
+
+
+
+
+ {{ countdown }}秒后重新获取
+
+
+ 重新获取验证码
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/login/wx-login.vue b/pages/login/wx-login.vue
new file mode 100644
index 0000000..2d45d61
--- /dev/null
+++ b/pages/login/wx-login.vue
@@ -0,0 +1,697 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 请选择您的角色
+
+
+
+
+
+ 个人
+
+
+
+
+
+ 单位
+
+
+
+
+
+
+ 请选择机构类型
+
+
+ {{ option.label }}
+
+
+
+
+
+
+
+
+ 保护您的个人信息安全
+
+
+
+ 为您推荐更合适的岗位
+
+
+
+ 享受完整的就业服务
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 我已阅读并同意
+
+ 《隐私协议》
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/mine/company-mine.vue b/pages/mine/company-mine.vue
index dc075ec..f718609 100644
--- a/pages/mine/company-mine.vue
+++ b/pages/mine/company-mine.vue
@@ -34,13 +34,61 @@
{{ companyInfo.isVerified ? '已通过' : '未认证' }}
-
+
+
+
+ 消息
+
+
+
+
+
+
通知与提醒
- 已开启
+
+
+
+
+
+
+ 评论与反馈
+
+
+
+
+
+
+
+
+ 培训机构维护
+
+ 修改
+
+
+
+
+ 评价机构维护
+
+ 修改
+
+
+
+
+ 培训公告发布
+
+ 发布
+
+
+
+
+ 评价公告发布
+
+ 发布
+
@@ -60,6 +108,39 @@
@close="close"
>
+
+
+
+
+
+
+
+
@@ -70,6 +151,21 @@ import useUserStore from '@/stores/useUserStore';
const { $api, navTo } = inject('globalFunction');
const popup = ref(null);
+const reminderPopup = ref(null);
+const feedbackPopup = ref(null);
+
+// 提醒设置
+const reminderEnabled = ref(true);
+const reminderFrequency = ref('realtime');
+const reminderOptions = ref([
+ { label: '实时提醒', value: 'realtime' },
+ { label: '每小时提醒', value: 'hourly' },
+ { label: '每天提醒', value: 'daily' }
+]);
+
+// 评论与反馈
+const feedbackContent = ref('');
+const rating = ref(0);
// 企业信息数据
const companyInfo = reactive({
@@ -82,6 +178,21 @@ const companyInfo = reactive({
function goToCompanyInfo() {
navTo('/pages/mine/company-info');
}
+function handleInstitutionClick(i){
+ if(i==1){
+ navTo('/packageB/institution/trainingInstitutionMaintenance');
+ }else if(i==2){
+ navTo('/packageB/institution/evaluationAgencyMaintenance');
+ }else if(i==3){
+ navTo('/packageB/notice/trainingAnnouncement/postedList');
+ }else if(i==4){
+ navTo('/packageB/notice/evaluateAnnouncement/evaluateList');
+ }
+}
+// 跳转到消息页面
+function goToMessage() {
+ navTo('/pages/msglog/msglog');
+}
function logOut() {
popup.value.open();
@@ -92,10 +203,7 @@ function close() {
}
function confirm() {
- // 调用退出登录
- useUserStore().logOut();
- // 关闭弹窗
- popup.value.close();
+ useUserStore().logOut(false); // 不显示登录弹窗
// 跳转到首页
uni.reLaunch({
url: '/pages/index/index'
@@ -111,9 +219,9 @@ onShow(() => {
onMounted(() => {
uni.$on('showLoginModal', () => {
// 这里可以显示微信登录弹窗
- // 由于这个页面没有 WxAuthLogin 组件,我们跳转到首页让首页处理
- uni.reLaunch({
- url: '/pages/index/index'
+ // 跳转到微信登录页面
+ uni.navigateTo({
+ url: '/pages/login/wx-login'
});
});
});
@@ -147,6 +255,57 @@ function getCompanyInfo() {
companyInfo.isVerified = false;
}
}
+
+// 切换提醒开启/关闭状态
+function toggleReminder(e) {
+ reminderEnabled.value = e.detail.value;
+}
+
+// 打开提醒设置弹窗
+function openReminderSettings() {
+ reminderPopup.value.open();
+}
+
+// 关闭提醒设置弹窗
+function closeReminderPopup() {
+ reminderPopup.value.close();
+}
+
+// 处理提醒频率变化
+function handleFrequencyChange(e) {
+ reminderFrequency.value = e.detail.value;
+}
+
+// 打开评论与反馈弹窗
+function openFeedbackPopup() {
+ feedbackPopup.value.open();
+}
+
+// 关闭评论与反馈弹窗
+function closeFeedbackPopup() {
+ feedbackPopup.value.close();
+}
+
+// 设置评分
+function setRating(score) {
+ rating.value = score;
+}
+
+// 提交反馈
+function submitFeedback() {
+ // 模拟提交成功
+ uni.showToast({
+ title: '反馈提交成功',
+ icon: 'success'
+ });
+ // 清空表单
+ feedbackContent.value = '';
+ rating.value = 0;
+ // 延迟关闭弹窗,确保用户能看到成功提示
+ setTimeout(() => {
+ closeFeedbackPopup();
+ }, 1000);
+}
diff --git a/pages/mine/mine.vue b/pages/mine/mine.vue
index 9ad6cba..2c2c9ad 100644
--- a/pages/mine/mine.vue
+++ b/pages/mine/mine.vue
@@ -47,6 +47,10 @@
{{ counts.fairCollecitonCount }}
预约
+
+ {{ counts.applyCencalCount || 0 }}
+ 取消投递
+
@@ -92,12 +96,32 @@
-
+
+
+
+ 消息
+
+
+
+
+
+
通知与提醒
- 已开启
+
+
+
+
+
+
+
+ 评论与反馈
+
+
+
+
求职帮
@@ -113,6 +137,39 @@
@close="close"
>
+
+
+
+
+
+
+
+
@@ -128,9 +185,24 @@ const { $api, navTo } = inject('globalFunction');
import useUserStore from '@/stores/useUserStore';
import { tabbarManager } from '@/utils/tabbarManager';
const popup = ref(null);
+const reminderPopup = ref(null);
+const feedbackPopup = ref(null);
const { userInfo, Completion } = storeToRefs(useUserStore());
const counts = ref({});
+// 提醒设置
+const reminderEnabled = ref(true);
+const reminderFrequency = ref('realtime');
+const reminderOptions = ref([
+ { label: '实时提醒', value: 'realtime' },
+ { label: '每小时提醒', value: 'hourly' },
+ { label: '每天提醒', value: 'daily' }
+]);
+
+// 评论与反馈
+const feedbackContent = ref('');
+const rating = ref(0);
+
// 获取用户类型,参考首页的实现方式
const userType = computed(() => {
// 优先从store获取,如果为空则从缓存获取
@@ -162,9 +234,9 @@ onShow(() => {
onMounted(() => {
uni.$on('showLoginModal', () => {
// 这里可以显示微信登录弹窗
- // 由于这个页面没有 WxAuthLogin 组件,我们跳转到首页让首页处理
- uni.reLaunch({
- url: '/pages/index/index'
+ // 跳转到微信登录页面
+ uni.navigateTo({
+ url: '/pages/login/wx-login'
});
});
});
@@ -178,7 +250,11 @@ function close() {
}
function confirm() {
- useUserStore().logOut();
+ useUserStore().logOut(false); // 不显示登录弹窗
+ // 跳转到首页
+ uni.reLaunch({
+ url: '/pages/index/index'
+ });
}
const isAbove90 = (percent) => parseFloat(percent) < 90;
@@ -201,7 +277,7 @@ function seeDetail() {
function goToJobHelper() {
// 跳转到求职者信息补全页面
- navTo('/pages/complete-info/complete-info');
+ navTo('/packageA/pages/complete-info/complete-info');
}
// 跳转到素质测评
function goCa(){
@@ -214,6 +290,62 @@ function goCaAI(){
navTo(`/packageCa/search/AIAudition?name=${userInfo.name}&userId=${userInfo.idCard}`);
}
+// 跳转到消息页面
+function goToMessage(){
+ navTo('/pages/msglog/msglog');
+}
+
+// 切换提醒开启/关闭状态
+function toggleReminder(e) {
+ reminderEnabled.value = e.detail.value;
+}
+
+// 打开提醒设置弹窗
+function openReminderSettings() {
+ reminderPopup.value.open();
+}
+
+// 关闭提醒设置弹窗
+function closeReminderPopup() {
+ reminderPopup.value.close();
+}
+
+// 处理提醒频率变化
+function handleFrequencyChange(e) {
+ reminderFrequency.value = e.detail.value;
+}
+
+// 打开评论与反馈弹窗
+function openFeedbackPopup() {
+ feedbackPopup.value.open();
+}
+
+// 关闭评论与反馈弹窗
+function closeFeedbackPopup() {
+ feedbackPopup.value.close();
+}
+
+// 设置评分
+function setRating(score) {
+ rating.value = score;
+}
+
+// 提交反馈
+function submitFeedback() {
+ // 模拟提交成功
+ uni.showToast({
+ title: '反馈提交成功',
+ icon: 'success'
+ });
+ // 清空表单
+ feedbackContent.value = '';
+ rating.value = 0;
+ // 延迟关闭弹窗,确保用户能看到成功提示
+ setTimeout(() => {
+ closeFeedbackPopup();
+ }, 1000);
+}
+
diff --git a/pages/msglog/msglog.vue b/pages/msglog/msglog.vue
index 84231cb..54b2d35 100644
--- a/pages/msglog/msglog.vue
+++ b/pages/msglog/msglog.vue
@@ -31,8 +31,7 @@
-
-
+
@@ -46,14 +45,14 @@ import Tabbar from '@/components/tabbar/midell-box.vue';
import ReadComponent from './read.vue';
import UnreadComponent from './unread.vue';
import { tabbarManager } from '@/utils/tabbarManager';
-import WxAuthLogin from '@/components/WxAuthLogin/WxAuthLogin.vue';
+
const loadedMap = reactive([false, false]);
const swiperRefs = [ref(null), ref(null)];
const components = [ReadComponent, UnreadComponent];
import { storeToRefs } from 'pinia';
import { useReadMsg } from '@/stores/useReadMsg';
const { unreadCount } = storeToRefs(useReadMsg());
-const wxAuthLoginRef = ref(null);
+
onShow(() => {
// 获取消息列表
@@ -71,7 +70,7 @@ onMounted(() => {
// 监听退出登录事件,显示微信登录弹窗
uni.$on('showLoginModal', () => {
- wxAuthLoginRef.value?.open();
+ uni.navigateTo({ url: '/pages/login/wx-login' });
});
});
diff --git a/pages/search/search.vue b/pages/search/search.vue
index c3e0fe6..f7a26bc 100644
--- a/pages/search/search.vue
+++ b/pages/search/search.vue
@@ -134,7 +134,7 @@ function nextDetail(job) {
const recordData = recommedIndexDb.JobParameter(job);
recommedIndexDb.addRecord(recordData);
}
- navTo(`/packageA/pages/post/post?jobId=${encodeURIComponent(job.jobId)}`);
+ navTo(`/packageA/pages/post/post?jobId=${encodeURIComponent(job.jobId)}&encryptJobId=${encodeURIComponent(job.encryptJobId)}`);
}
function nextVideo(job) {
diff --git a/pages/service/career-planning.bak.vue b/pages/service/career-planning.bak.vue
new file mode 100644
index 0000000..c3abf8d
--- /dev/null
+++ b/pages/service/career-planning.bak.vue
@@ -0,0 +1,590 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/service/career-planning.vue b/pages/service/career-planning.vue
index c3abf8d..ed03978 100644
--- a/pages/service/career-planning.vue
+++ b/pages/service/career-planning.vue
@@ -1,67 +1,4 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/static/tabbar/robot2.png b/static/tabbar/robot2.png
new file mode 100644
index 0000000..ed89ff6
Binary files /dev/null and b/static/tabbar/robot2.png differ
diff --git a/stores/useCareerPathStore.js b/stores/useCareerPathStore.js
new file mode 100644
index 0000000..2f4a6dd
--- /dev/null
+++ b/stores/useCareerPathStore.js
@@ -0,0 +1,233 @@
+import { computed, ref, watch } from 'vue';
+import { defineStore, storeToRefs } from 'pinia';
+import useUserStore from '@/stores/useUserStore';
+import { getCurrentPosition, getPath, getPathDetail } from '@/apiRc/service/careerPath';
+
+
+export const useCareerPathStore = defineStore('career-path', () => {
+ const { userInfo: ui } = storeToRefs(useUserStore());
+ const userInfo = ref({
+ userName: '',
+ professions: [],
+ skills: []
+ });
+
+ watch(() => ui.value, () => {
+ if (!ui.value) {
+ return;
+ }
+ try {
+ const { jobTitle, appSkillsList } = ui.value;
+
+ userInfo.value.professions = jobTitle.map((d) => {
+ return {
+ label: d,
+ value: d
+ };
+ });
+ userInfo.value.skills = appSkillsList.map((d) => {
+ return {
+ label: d.name,
+ value: d.name
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ }, {
+ immediate: true,
+ deep: true
+ });
+
+ const professionIndex = ref(0);
+ const profession = ref('');
+ const professionLabel = ref('');
+ const professions = ref([]);
+ const professionsRef = computed(() => {
+ if (!userInfo.value || !userInfo.value.professions || userInfo.value.professions.length === 0) {
+ return professions.value;
+ }
+ const userProfessionsLabels = userInfo.value.professions.map((d) => d.label);
+ let professionsA = [];
+ let professionsB = [];
+ professions.value.filter((d) => userProfessionsLabels.includes(d.label));
+ for (const d of professions.value) {
+ if (userProfessionsLabels.includes(d.label)) {
+ professionsA.push(d);
+ } else {
+ professionsB.push(d);
+ }
+ }
+ if (professionsA.length === 0) {
+ professionsA = userInfo.value.professions;
+ professionsB = professions.value;
+ }
+ return [ ...professionsA, ...professionsB ];
+ });
+
+ const targetCareerIndex = ref(0);
+ const targetCareer = ref('');
+ const targetCareerLabel = ref('');
+ const paths = ref([]);
+ const pathsRef = computed(() => {
+ return paths.value.filter((d) => {
+ return `${ d.startJobId }` === profession.value;
+ });
+ });
+
+ const result = ref([]);
+
+ const fetchData = async () => {
+ try {
+ const { code, msg, data } = await getCurrentPosition();
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ professions.value = data.map((d) => {
+ return {
+ label: d.name,
+ value: `${ d.jobId }`
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const fetchDataPath = async () => {
+ try {
+ const { code, msg, data } = await getPath();
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ paths.value = data.map((d) => {
+ return {
+ label: d.endJob,
+ value: `${ d.startJobId }-${ d.endJobId }`,
+ startJobId: d.startJobId
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const fetchResult = async () => {
+ if (!targetCareer.value) {
+ return;
+ }
+ const [ startJobId, endJobId ] = targetCareer.value.split('-');
+ const params = {
+ startJobId: Number(startJobId),
+ endJobId: Number(endJobId)
+ };
+ try {
+ const { code, msg, data } = await getPathDetail(params);
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ result.value = data.map((d, i) => {
+ return {
+ type: i === 0 ? 'start' : i === data.length - 1 ? 'end' : 'step',
+ step: i,
+ title: d.name,
+ tags: d.skillNameList.split(',')
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const eventSearch = () => {
+ if (pathsRef.value.length === 0) {
+ uni.showToast({
+ title: '当前职业暂无发展路径,敬请期待!',
+ icon: 'none'
+ });
+ return;
+ }
+ if (!profession.value) {
+ uni.showToast({
+ title: '请选择当前职位!',
+ icon: 'none'
+ });
+ return;
+ }
+ if (!targetCareer.value) {
+ uni.showToast({
+ title: '请选择目标职业!',
+ icon: 'none'
+ });
+ return;
+ }
+ void fetchResult();
+ };
+
+ const eventProfession = (e) => {
+ professionIndex.value = Number(e.detail.value);
+ const item = professionsRef.value[e.detail.value]
+ profession.value = item.value;
+ professionLabel.value = item.label;
+ targetCareer.value = '';
+ targetCareerLabel.value = '';
+ result.value = [];
+ };
+
+ const eventTargetCareer = (e) => {
+ targetCareerIndex.value = Number(e.detail.value);
+ const item = pathsRef.value[e.detail.value]
+ targetCareer.value = item.value;
+ targetCareerLabel.value = item.label;
+ result.value = [];
+ };
+
+ void fetchData();
+ void fetchDataPath();
+
+ watch(
+ () => professionsRef.value,
+ () => {
+ if (professionsRef.value[ 0 ]) {
+ profession.value = professionsRef.value[ 0 ].value;
+ professionLabel.value = professionsRef.value[ 0 ].label;
+ }
+ }
+ );
+
+ return {
+ professionIndex,
+ professionLabel,
+ profession,
+ professionsRef,
+ targetCareerIndex,
+ targetCareer,
+ targetCareerLabel,
+ pathsRef,
+ result,
+ eventProfession,
+ eventTargetCareer,
+ eventSearch
+ };
+});
diff --git a/stores/useCareerRecommendationStore.js b/stores/useCareerRecommendationStore.js
new file mode 100644
index 0000000..519c7dd
--- /dev/null
+++ b/stores/useCareerRecommendationStore.js
@@ -0,0 +1,197 @@
+import { computed, ref, watch } from 'vue';
+import { defineStore, storeToRefs } from 'pinia';
+import useUserStore from '@/stores/useUserStore';
+import { getProfessions, getRecommend, getSkillTags } from '@/apiRc/service/careerRecommendation';
+
+
+export const useCareerRecommendationStore = defineStore('career-recommendation', () => {
+ const { userInfo: ui } = storeToRefs(useUserStore());
+ const userInfo = ref({
+ userName: '',
+ professions: [],
+ skills: []
+ });
+
+ watch(() => ui.value, () => {
+ if (!ui.value) {
+ return;
+ }
+ try {
+ const { jobTitle, appSkillsList } = ui.value;
+
+ userInfo.value.professions = jobTitle.map((d) => {
+ return {
+ label: d,
+ value: d
+ };
+ });
+ userInfo.value.skills = appSkillsList.map((d) => {
+ return {
+ label: d.name,
+ value: d.name
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ }, {
+ immediate: true,
+ deep: true
+ });
+
+ const professionIndex = ref(0);
+ const profession = ref('');
+ const professionLabel = ref('');
+ const professions = ref([]);
+ const professionsRef = computed(() => {
+ if (!userInfo.value || !userInfo.value.professions || userInfo.value.professions.length === 0) {
+ return professions.value;
+ }
+ const userProfessionsLabels = userInfo.value.professions.map((d) => d.label);
+ let professionsA = [];
+ let professionsB = [];
+ professions.value.filter((d) => userProfessionsLabels.includes(d.label));
+ for (const d of professions.value) {
+ if (userProfessionsLabels.includes(d.label)) {
+ professionsA.push(d);
+ } else {
+ professionsB.push(d);
+ }
+ }
+ if (professionsA.length === 0) {
+ professionsA = userInfo.value.professions;
+ professionsB = professions.value;
+ }
+ return [ ...professionsA, ...professionsB ];
+ });
+
+ const skills = ref([]);
+ const skillTags = computed(() => {
+ if (userInfo.value.professions[ 0 ] && professionLabel.value === userInfo.value.professions[ 0 ].value) {
+ return userInfo.value.skills.map((d) => d.label);
+ }
+ return skills.value;
+ });
+
+ const result = ref([]);
+
+ const fetchData = async () => {
+ try {
+ const { code, msg, data } = await getProfessions();
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ professions.value = data.map((d) => {
+ return {
+ label: d.name,
+ value: d.name
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const fetchSkillTags = async () => {
+ const params = {
+ jobName: professionLabel.value
+ };
+ try {
+ const { code, msg, data } = await getSkillTags(params);
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (typeof data !== 'undefined' && Array.isArray(data) && data.length > 0 && data[ 0 ]) {
+ skills.value = data[ 0 ].skillDetList.map((d) => d.skillName);
+ }
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const fetchRecommend = async () => {
+ const params = {
+ jobName: professionLabel.value
+ };
+ try {
+ const { code, msg, data } = await getRecommend(params);
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ result.value = data.map((d) => {
+ return {
+ title: d.jobName,
+ tags: d.skillList.map((d) => d.skillName),
+ percentage: d.similarityScore ?? 0
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const eventSearch = () => {
+ void fetchRecommend();
+ };
+
+ const eventProfession = (e) => {
+ professionIndex.value = Number(e.detail.value);
+ const item = professionsRef.value[ e.detail.value ];
+ profession.value = item.value;
+ professionLabel.value = item.label;
+ };
+
+ void fetchData();
+
+ watch(
+ () => profession.value,
+ () => {
+ if (profession.value) {
+ void fetchSkillTags();
+ result.value = [];
+ }
+ },
+ {
+ immediate: true
+ }
+ );
+
+ watch(
+ () => professionsRef.value,
+ () => {
+ if (professionsRef.value[ 0 ]) {
+ profession.value = professionsRef.value[ 0 ].value;
+ professionLabel.value = professionsRef.value[ 0 ].label;
+ }
+ }
+ );
+
+ return {
+ professionIndex,
+ profession,
+ professionLabel,
+ professionsRef,
+ skillTags,
+ result,
+ eventProfession,
+ eventSearch
+ };
+});
diff --git a/stores/useDictStore.js b/stores/useDictStore.js
index eeead1d..f74bac9 100644
--- a/stores/useDictStore.js
+++ b/stores/useDictStore.js
@@ -50,7 +50,7 @@ const useDictStore = defineStore("dict", () => {
return data
})
}
- const [education, experience, area, scale, sex, affiliation, nature, noticeType] =
+ const [education, experience, area, scale, sex, affiliation, nature, noticeType] =
await Promise.all([
getDictSelectOption('education'),
getDictSelectOption('experience'),
@@ -74,6 +74,17 @@ const useDictStore = defineStore("dict", () => {
getIndustryDict() // 获取行业
} catch (error) {
console.error('Error fetching dictionary data:', error);
+ // 确保即使出错也能返回空数组
+ if (!dictType && !dictName) {
+ state.education = [];
+ state.experience = [];
+ state.area = [];
+ state.scale = [];
+ state.sex = [];
+ state.affiliation = [];
+ state.nature = [];
+ state.noticeType = [];
+ }
}
};
@@ -97,31 +108,31 @@ const useDictStore = defineStore("dict", () => {
return null
}
- function dictLabel(dictType, value) {
- if (state[dictType] && Array.isArray(state[dictType])) {
- for (let i = 0; i < state[dictType].length; i++) {
- let element = state[dictType][i];
- if (element.value === value) {
- return element.label
- }
- }
- }
- return ''
+ function dictLabel(dictType, value) {
+ if (state[dictType] && Array.isArray(state[dictType])) {
+ for (let i = 0; i < state[dictType].length; i++) {
+ let element = state[dictType][i];
+ if (element.value === value) {
+ return element.label
+ }
+ }
+ }
+ return ''
}
- function oneDictData(dictType, value) {
- if (!value) {
- return state[dictType]
- }
- if (state[dictType]) {
- for (let i = 0; i < state[dictType].length; i++) {
- let element = state[dictType][i];
- if (element.value === value) {
- return element
- }
- }
- }
- return null
+ function oneDictData(dictType, value) {
+ if (!value) {
+ return state[dictType]
+ }
+ if (state[dictType]) {
+ for (let i = 0; i < state[dictType].length; i++) {
+ let element = state[dictType][i];
+ if (element.value === value) {
+ return element
+ }
+ }
+ }
+ return null
}
function getTransformChildren(dictType, title = '', key = '') {
@@ -129,7 +140,7 @@ const useDictStore = defineStore("dict", () => {
return {
label: title,
key: key || dictType,
- options: state[dictType],
+ options: state[dictType] || [],
}
}
return null
diff --git a/stores/useSkillDevelopmentStore.js b/stores/useSkillDevelopmentStore.js
new file mode 100644
index 0000000..6bda9fe
--- /dev/null
+++ b/stores/useSkillDevelopmentStore.js
@@ -0,0 +1,303 @@
+import { computed, ref, watch } from 'vue';
+import { defineStore, storeToRefs } from 'pinia';
+import useUserStore from '@/stores/useUserStore';
+import { getCurrentPosition, getPath } from '@/apiRc/service/careerPath';
+import { getCareerPath, getSkillResult } from '@/apiRc/service/skillDevelopment';
+
+
+export const useSkillDevelopmentStore = defineStore('skill-development', () => {
+ const { userInfo: ui } = storeToRefs(useUserStore());
+ const userInfo = ref({
+ userName: '',
+ professions: [],
+ skills: []
+ });
+
+ watch(() => ui.value, () => {
+ if (!ui.value) {
+ return;
+ }
+ try {
+ const { jobTitle, appSkillsList } = ui.value;
+
+ userInfo.value.professions = jobTitle.map((d) => {
+ return {
+ label: d,
+ value: d
+ };
+ });
+ userInfo.value.skills = appSkillsList.map((d) => {
+ return {
+ label: d.name,
+ value: d.name
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ }, {
+ immediate: true,
+ deep: true
+ });
+
+ const professionIndex = ref(0);
+ const profession = ref('');
+ const professionLabel = ref('');
+ const professions = ref([]);
+ const professionsRef = computed(() => {
+ if (!userInfo.value || !userInfo.value.professions || userInfo.value.professions.length === 0) {
+ return professions.value;
+ }
+ const userProfessionsLabels = userInfo.value.professions.map((d) => d.label);
+ let professionsA = [];
+ let professionsB = [];
+ professions.value.filter((d) => userProfessionsLabels.includes(d.label));
+ for (const d of professions.value) {
+ if (userProfessionsLabels.includes(d.label)) {
+ professionsA.push(d);
+ } else {
+ professionsB.push(d);
+ }
+ }
+ if (professionsA.length === 0) {
+ professionsA = userInfo.value.professions;
+ professionsB = professions.value;
+ }
+ return [ ...professionsA, ...professionsB ];
+ });
+
+ const targetCareerIndex = ref(0);
+ const targetCareer = ref('');
+ const targetCareerLabel = ref('');
+ const paths = ref([]);
+ const pathsRef = computed(() => {
+ return paths.value.filter((d) => {
+ const [startJobId] = d.value.split('-');
+ return startJobId === profession.value;
+ });
+ });
+
+ const careerPaths = ref([]);
+ const currentCareer = ref(null);
+ const currentCareerIndex = ref(0);
+ const currentCareerLabel = computed(() => {
+ if (!currentCareer.value) {
+ return '';
+ }
+ return currentCareer.value.label;
+ });
+
+ const result = ref([]);
+
+ const fetchData = async () => {
+ try {
+ const { code, msg, data } = await getCurrentPosition();
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ professions.value = data.map((d) => {
+ return {
+ label: d.name,
+ value: `${ d.jobId }`
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const fetchDataPath = async () => {
+ try {
+ const { code, msg, data } = await getPath();
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ paths.value = data.map((d) => {
+ return {
+ label: d.endJob,
+ value: `${d.startJobId}-${d.endJobId}`
+
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const fetchCareerPaths = async () => {
+ if (!targetCareer.value) {
+ return;
+ }
+ const [startJobId, endJobId] = targetCareer.value.split('-');
+ const params = {
+ startJobId,
+ endJobId
+ };
+ try {
+ const { code, msg, data } = await getCareerPath(params);
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ careerPaths.value = data.map((d, i) => {
+ let index = `${i}`;
+ let fontSize = 26;
+ if (i === 0) {
+ index = '起点';
+ fontSize = 18;
+ }
+ if (i === data.length - 1) {
+ index = '终点';
+ fontSize = 18;
+ }
+ return {
+ index,
+ label: d.name,
+ value: d.jobId,
+ fontSize
+ };
+ });
+ if (careerPaths.value[0]) {
+ void eventResult(0);
+ }
+ } catch (e) {
+ console.warn(e);
+ careerPaths.value = [];
+ }
+ };
+
+ const eventResult = async (index) => {
+ currentCareerIndex.value = index;
+ currentCareer.value = careerPaths.value[index] ?? null;
+
+ const params = {
+ jobId: currentCareer.value?.value
+ };
+ try {
+ const { code, msg, data } = await getSkillResult(params);
+ if (code !== 0) {
+ uni.showToast({
+ title: msg,
+ icon: 'none'
+ });
+ return;
+ }
+ if (!data) {
+ return;
+ }
+ result.value = data.map((d) => {
+ return {
+ label: d.secDimName,
+ value: d.secDimId,
+ children: d.skillDetList.map((d) => {
+ return {
+ label: d.skillName,
+ value: d.skillScore,
+ weight: d.skillWeight
+ };
+ })
+ };
+ });
+ } catch (e) {
+ console.warn(e);
+ }
+ };
+
+ const eventSearch = () => {
+ if (pathsRef.value.length === 0) {
+ uni.showToast({
+ title: '当前职业暂无发展路径,敬请期待!',
+ icon: 'none'
+ });
+ return;
+ }
+ if (!profession.value) {
+ uni.showToast({
+ title: '请选择当前职位!',
+ icon: 'none'
+ });
+ return;
+ }
+ if (!targetCareer.value) {
+ uni.showToast({
+ title: '请选择目标职业!',
+ icon: 'none'
+ });
+ return;
+ }
+ void fetchCareerPaths();
+ };
+
+ const eventProfession = (e) => {
+ professionIndex.value = Number(e.detail.value);
+ const item = professionsRef.value[ e.detail.value ];
+ profession.value = item.value;
+ professionLabel.value = item.label;
+ targetCareer.value = '';
+ targetCareerLabel.value = '';
+ careerPaths.value = [];
+ result.value = [];
+ };
+
+ const eventTargetCareer = (e) => {
+ targetCareerIndex.value = Number(e.detail.value);
+ const item = pathsRef.value[ e.detail.value ];
+ targetCareer.value = item.value;
+ targetCareerLabel.value = item.label;
+ careerPaths.value = [];
+ result.value = [];
+ };
+
+ void fetchData();
+ void fetchDataPath();
+
+ watch(
+ () => professionsRef.value,
+ () => {
+ if (professionsRef.value[ 0 ]) {
+ profession.value = professionsRef.value[ 0 ].value;
+ professionLabel.value = professionsRef.value[ 0 ].label;
+ }
+ }
+ );
+
+ return {
+ professionIndex,
+ professionLabel,
+ profession,
+ professionsRef,
+ targetCareerIndex,
+ targetCareer,
+ targetCareerLabel,
+ pathsRef,
+ careerPaths,
+ currentCareer,
+ currentCareerLabel,
+ currentCareerIndex,
+ result,
+ eventProfession,
+ eventTargetCareer,
+ eventSearch,
+ eventResult
+ };
+});
diff --git a/stores/useUserStore.js b/stores/useUserStore.js
index 5fc5864..9388388 100644
--- a/stores/useUserStore.js
+++ b/stores/useUserStore.js
@@ -74,10 +74,17 @@ const useUserStore = defineStore("user", () => {
uni.removeStorageSync('token')
uni.removeStorageSync('Padmin-Token')
// 如果需要显示登录弹窗,则通过事件通知页面显示微信登录弹窗
- if (showLoginModal) {
- // 通过 uni.$emit 发送全局事件,通知页面显示登录弹窗
- uni.$emit('showLoginModal');
- }
+ // if (showLoginModal) {
+ // // 通过 uni.$emit 发送全局事件,通知页面显示登录弹窗
+ // uni.$emit('showLoginModal');
+ // }
+ //#ifdef H5
+ // 跳转到首页
+ window.location.href = 'https://www.xjksly.cn/mechine-single-vue/';
+ //#endif
+ uni.reLaunch({
+ url: '/pages/index/index'
+ });
}
const getUserInfo = () => {
@@ -96,7 +103,13 @@ const useUserStore = defineStore("user", () => {
similarityJobs.setUserInfo(resume.data)
setUserInfo(resume);
reslove(resume)
- }).catch(() => reject());
+ }).catch((error) => {
+ // 对于企业用户,简历接口可能失败,但这不应该阻止登录流程
+ // 记录错误但不reject,让登录流程继续
+ console.warn('获取简历信息失败,可能是企业用户或无简历信息:', error);
+ // 返回一个空的简历对象,让登录流程继续
+ reslove({ data: {} });
+ });
})
}
diff --git a/stores/userChatGroupStore.js b/stores/userChatGroupStore.js
index 9eb330f..edeab52 100644
--- a/stores/userChatGroupStore.js
+++ b/stores/userChatGroupStore.js
@@ -113,13 +113,19 @@ const useChatGroupDBStore = defineStore("messageGroup", () => {
try {
toggleTyping(true);
const customDataID = 'message_' + UUID.generate()
+
+ // 对话历史管理:只保留最近的N条消息,防止token超限
+ // 计算消息数量,只保留最近的10条消息(可根据实际情况调整)
+ const MAX_HISTORY_MESSAGES = 10;
+ const historyMessages = messages.value.slice(-MAX_HISTORY_MESSAGES);
+
const params = {
data: text,
sessionId: chatSessionID.value,
dataId: customDataID
};
if (fileUrls && fileUrls.length) {
- params['fileUrl'] = fileUrls.map((item) => item.url);
+ params['fileUrl'] = fileUrls.map((item) => item.serverPath || item.url);
}
// ------>
const MsgData = {
@@ -329,6 +335,72 @@ const useChatGroupDBStore = defineStore("messageGroup", () => {
}))
}
+ // 删除消息
+ async function deleteMessages(indices) {
+ // 按索引从大到小排序,避免删除时索引变化
+ const sortedIndices = [...indices].sort((a, b) => b - a);
+
+ // 从内存中删除消息
+ for (const index of sortedIndices) {
+ const message = messages.value[index];
+ if (message && message.id) {
+ // 从本地数据库中删除
+ await baseDB.db.delete(massageName.value, message.id);
+ }
+ messages.value.splice(index, 1);
+ }
+ }
+
+ // 删除会话
+ async function deleteDialogue(sessionId) {
+ if (!baseDB.isDBReady) await baseDB.initDB();
+
+ // 删除会话下的所有消息
+ const messageList = await baseDB.db.queryByField(massageName.value, 'parentGroupId', sessionId);
+ for (const message of messageList) {
+ if (message.id) {
+ await baseDB.db.delete(massageName.value, message.id);
+ }
+ }
+
+ // 删除会话本身
+ const sessionList = await baseDB.db.queryByField(tableName.value, 'sessionId', sessionId);
+ for (const session of sessionList) {
+ if (session.id) {
+ await baseDB.db.delete(tableName.value, session.id);
+ }
+ }
+
+ // 重新获取所有会话并重新分组
+ const allSessions = await baseDB.db.getAll(tableName.value);
+ if (allSessions.length) {
+ const [result, lastData] = insertSortData(allSessions);
+ tabeList.value = result;
+ } else {
+ tabeList.value = [];
+ }
+
+ // 如果删除的是当前会话,切换到第一个会话或清空
+ if (chatSessionID.value === sessionId) {
+ if (tabeList.value.length > 0) {
+ // 找到第一个非标题的会话
+ const firstSession = tabeList.value.find(item => !item.isTitle);
+ if (firstSession) {
+ chatSessionID.value = firstSession.sessionId;
+ await initMessage(firstSession.sessionId);
+ } else {
+ // 没有会话了
+ chatSessionID.value = '';
+ messages.value = [];
+ }
+ } else {
+ // 没有会话了
+ chatSessionID.value = '';
+ messages.value = [];
+ }
+ }
+ }
+
return {
messages,
isTyping,
@@ -342,7 +414,9 @@ const useChatGroupDBStore = defineStore("messageGroup", () => {
changeDialogue,
getStearm,
getHistory,
- badFeedback
+ badFeedback,
+ deleteMessages,
+ deleteDialogue
};
});
diff --git a/uni_modules/custom-waterfalls-flow/components/custom-waterfalls-flow/custom-waterfalls-flow.vue b/uni_modules/custom-waterfalls-flow/components/custom-waterfalls-flow/custom-waterfalls-flow.vue
index c4eb37c..3a7ca4f 100644
--- a/uni_modules/custom-waterfalls-flow/components/custom-waterfalls-flow/custom-waterfalls-flow.vue
+++ b/uni_modules/custom-waterfalls-flow/components/custom-waterfalls-flow/custom-waterfalls-flow.vue
@@ -100,7 +100,7 @@ export default {
return {
data: {
list: this.value ? this.value : [],
- column: this.column < 2 ? 2 : this.column,
+ column: this.column < 1 ? 1 : this.column,
columnSpace: this.columnSpace <= 5 ? this.columnSpace : 5,
imageKey: this.imageKey,
seat: this.seat,
@@ -179,7 +179,7 @@ export default {
this.isRefresh = true;
this.adds = [];
this.data.list = this.value ? this.value : [];
- this.data.column = this.column < 2 ? 2 : this.column >= this.maxColumn ? this.maxColumn : this.column;
+ this.data.column = this.column < 1 ? 1 : this.column >= this.maxColumn ? this.maxColumn : this.column;
this.data.columnSpace = this.columnSpace <= 5 ? this.columnSpace : 5;
this.data.imageKey = this.imageKey;
this.data.seat = this.seat;
diff --git a/utils/loginHelper.js b/utils/loginHelper.js
index 919567d..4f19bb3 100644
--- a/utils/loginHelper.js
+++ b/utils/loginHelper.js
@@ -44,24 +44,11 @@ export function navigateToLoginPage(options = {}) {
switch (platform) {
case 'mp-weixin':
- // 小程序端使用微信授权登录,直接显示微信授权弹窗
- uni.$emit('showLoginModal', { loginType: 'wechat' });
- return;
-
- case 'h5':
- if (loginType === 'idCard') {
- // H5端身份证号码登录
- loginPage = '/pages/login/id-card-login';
- } else {
- // H5端账号密码登录
- loginPage = '/pages/login/h5-login';
- }
- break;
-
case 'app':
- // App端使用微信授权登录
- uni.$emit('showLoginModal', { loginType: 'wechat' });
- return;
+ case 'h5':
+ // 跳转到微信登录页面
+ loginPage = '/pages/login/wx-login';
+ break;
default:
loginPage = '/pages/login/h5-login';
diff --git a/utils/markdownParser.js b/utils/markdownParser.js
index 1611be9..59e91b0 100644
--- a/utils/markdownParser.js
+++ b/utils/markdownParser.js
@@ -1,135 +1,195 @@
-import MarkdownIt from '@/lib/markdown-it.min.js';
-import hljs from "@/lib/highlight/highlight-uni.min.js";
-import parseHtml from '@/lib/html-parser.js';
-// import DOMPurify from '@/lib/dompurify@3.2.4es.js';
-
-export let codeDataList = []
-export let jobMoreMap = new Map()
-
-const md = new MarkdownIt({
- html: true, // 允许 HTML 标签
- linkify: true, // 自动解析 URL
- typographer: true, // 美化标点符号
- tables: true,
- breaks: true, // 让 \n 自动换行
- langPrefix: 'language-', // 代码高亮前缀
- // 如果结果以
-
-
${result.jobTitle}
-
${result.salary}
-
- ${result.location}·${result.companyName}
-
-
-
${result.education}
-
${result.experience}
-
-
-
-
- `
- if (result.data) {
- jobMoreMap.set(jobId, result.data)
- domContext +=
- `查看更多岗位`
- }
- return domContext
- }
- }
- // ${result.location}
- // ${result.salary}
- // 代码块
- let preCode = ""
- try {
- preCode = hljs.highlightAuto(str).value
- } catch (err) {
- preCode = markdownIt.utils.escapeHtml(str);
- }
- // 以换行进行分割 , 按行拆分代码
- const lines = preCode.split(/\n/).slice(0, -1);
- const html = lines
- .map((line, index) =>
- line ?
- `
${line}` :
- ''
- )
- .join('');
-
- // 代码复制功能
- const cacheIndex = codeDataList.length;
- codeDataList.push(str);
- return `
-
- `;
- }
-})
-
-function extractFirstJson(text) {
- let stack = [];
- let startIndex = -1;
- let endIndex = -1;
-
- for (let i = 0; i < text.length; i++) {
- const char = text[i];
-
- if (char === '{') {
- if (stack.length === 0) startIndex = i; // 记录第一个 '{' 的位置
- stack.push(char);
- } else if (char === '}') {
- stack.pop();
- if (stack.length === 0) {
- endIndex = i; // 找到配对的 '}'
- break;
- }
- }
- }
-
- if (startIndex !== -1 && endIndex !== -1) {
- const jsonString = text.slice(startIndex, endIndex + 1);
- try {
- const jsonObject = JSON.parse(jsonString);
- return jsonObject;
- } catch (e) {
- return null; // 如果不是有效的 JSON
- }
- }
-
- return null; // 如果没有找到有效的 JSON 对象
-}
-
-
-function safeExtractJson(text) {
- try {
- const jsonObject = extractFirstJson(text);
- return jsonObject
- } catch (e) {
- console.error('JSON 解析失败:', e);
- }
- return null;
-}
-
-export function clearJobMoreMap() { // 切换对话清空
- jobMoreMap.clear()
-}
-
-export function parseMarkdown(content) {
- if (!content) {
- return //处理特殊情况,比如网络异常导致的响应的 content 的值为空
- }
- codeDataList = []
- const unsafeHtml = md.render(content || '')
- return unsafeHtml
-}
\ No newline at end of file
+import MarkdownIt from '@/lib/markdown-it.min.js';
+import hljs from "@/lib/highlight/highlight-uni.min.js";
+import parseHtml from '@/lib/html-parser.js';
+// import DOMPurify from '@/lib/dompurify@3.2.4es.js';
+
+export let codeDataList = []
+export let jobMoreMap = new Map()
+export let jobCardsList = []
+
+const md = new MarkdownIt({
+ html: true, // 允许 HTML 标签
+ linkify: true, // 自动解析 URL
+ typographer: true, // 美化标点符号
+ tables: true,
+ breaks: true, // 让 \n 自动换行
+ langPrefix: 'language-', // 代码高亮前缀
+ // 如果结果以 ${result.jobTitle}${result.salary}
${result.location}·${result.companyName}
${result.education}
${result.experience}
`
+ if (result.data) {
+ jobMoreMap.set(jobId, result.data)
+ domContext += `查看更多岗位`
+ }
+ return domContext
+ }
+ }
+ // 代码块
+ let preCode = ""
+ try {
+ preCode = hljs.highlightAuto(str).value
+ } catch (err) {
+ preCode = md.utils.escapeHtml(str);
+ }
+ // 以换行进行分割 , 按行拆分代码
+ const lines = preCode.split(/\n/).slice(0, -1);
+ const html = lines
+ .map((line, index) =>
+ line ?
+ `
${line}` :
+ ''
+ )
+ .join('');
+
+ // 代码复制功能
+ const cacheIndex = codeDataList.length;
+ codeDataList.push(str);
+ return `
+
+ `;
+ }
+})
+
+function extractFirstJson(text) {
+ let stack = [];
+ let startIndex = -1;
+ let endIndex = -1;
+
+ for (let i = 0; i < text.length; i++) {
+ const char = text[i];
+
+ if (char === '{') {
+ if (stack.length === 0) startIndex = i; // 记录第一个 '{' 的位置
+ stack.push(char);
+ } else if (char === '}') {
+ stack.pop();
+ if (stack.length === 0) {
+ endIndex = i; // 找到配对的 '}'
+ break;
+ }
+ }
+ }
+
+ if (startIndex !== -1 && endIndex !== -1) {
+ const jsonString = text.slice(startIndex, endIndex + 1);
+ try {
+ const jsonObject = JSON.parse(jsonString);
+ return jsonObject;
+ } catch (e) {
+ return null; // 如果不是有效的 JSON
+ }
+ }
+
+ return null; // 如果没有找到有效的 JSON 对象
+}
+
+
+function safeExtractJson(text) {
+ try {
+ const jsonObject = extractFirstJson(text);
+ return jsonObject
+ } catch (e) {
+ console.error('JSON 解析失败:', e);
+ }
+ return null;
+}
+
+export function clearJobMoreMap() { // 切换对话清空
+ jobMoreMap.clear()
+}
+
+export function parseMarkdown(content) {
+ if (!content) {
+ return [] //处理特殊情况,比如网络异常导致的响应的 content 的值为空
+ }
+
+ // 过滤掉标签及其内容,这些是AI内部思考过程,不应该显示给用户
+ // 1. 处理原始标签(支持多行)
+ content = content.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '')
+ // 2. 处理HTML编码的标签
+ content = content.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '')
+ // 3. 处理部分编码的标签
+ content = content.replace(/<\s*think\s*>/gi, '')
+ content = content.replace(/<\s*\/\s*think\s*>/gi, '')
+
+ codeDataList = []
+ jobCardsList = [] // 清空岗位卡片列表,避免重复
+ const unsafeHtml = md.render(content || '')
+
+ // 在markdown渲染后再次过滤,确保没有遗漏
+ let filteredHtml = unsafeHtml
+ // 1. 处理原始标签(支持多行)
+ filteredHtml = filteredHtml.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '')
+ // 2. 处理HTML编码的标签
+ filteredHtml = filteredHtml.replace(/<\s*think\s*>[\s\S]*?<\s*\/\s*think\s*>/gi, '')
+ // 3. 处理部分编码的标签
+ filteredHtml = filteredHtml.replace(/<\s*think\s*>/gi, '')
+ filteredHtml = filteredHtml.replace(/<\s*\/\s*think\s*>/gi, '')
+ // 4. 单独处理剩余的think标签对
+ filteredHtml = filteredHtml.replace(/<think>/gi, '')
+ filteredHtml = filteredHtml.replace(/<\/think>/gi, '')
+ filteredHtml = filteredHtml.replace(//gi, '')
+ filteredHtml = filteredHtml.replace(/<\/think>/gi, '')
+
+ // 根据平台返回不同的内容格式
+ // 微信小程序:返回rich-text组件支持的nodes格式
+ // H5:直接返回HTML字符串,避免HTML解析错误
+ if (process.env.UNI_PLATFORM === 'mp-weixin') {
+ try {
+ return parseHtml(filteredHtml)
+ } catch (error) {
+ console.error('HTML解析失败:', error)
+ // 解析失败时返回空数组,避免页面崩溃
+ return []
+ }
+ } else {
+ // H5端直接返回HTML字符串
+ return filteredHtml
+ }
+}
diff --git a/utils/request.js b/utils/request.js
index c41e10a..648f0fc 100644
--- a/utils/request.js
+++ b/utils/request.js
@@ -155,7 +155,7 @@ export function uploadFile(tempFilePaths, loading = false) {
header["Authorization"] = encodeURIComponent(Authorization);
return new Promise((resolve, reject) => {
uni.uploadFile({
- url: config.baseUrl + '/app/file/upload',
+ url: config.baseUrl + '/app/file/uploadFile',
filePath: tempFilePaths,
name: 'file',
header,
@@ -176,11 +176,16 @@ export function uploadFile(tempFilePaths, loading = false) {
})
}
-export function myRequest(url, data = {}, method = 'GET', port = 9100, headers = {}, loading = false) {
+export function myRequest(url, data = {}, method = 'GET', port = 9100, headers = {}, loading = false,urlType='') {
let LCBaseUrl = config.LCBaseUrl
if (port != 9100) {
LCBaseUrl = config.LCBaseUrlInner
}
+ if(urlType=='jobRecommend'){
+ LCBaseUrl=config.jobRecommendUrl
+ }else if(urlType=='policyRecommend'){
+ LCBaseUrl=config.policyRecommendUrl
+ }
const header = headers || {};
// 上下文
// /jobfair-api/jobfair/public 招聘会
diff --git a/utils/streamRequest.js b/utils/streamRequest.js
index 9a49cef..19a517c 100644
--- a/utils/streamRequest.js
+++ b/utils/streamRequest.js
@@ -27,6 +27,7 @@ function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onCom
return new Promise((resolve, reject) => {
let buffer = '';
+ let hasReceivedContent = false;
const requestTask = uni.request({
url: config.StreamBaseURl + url,
@@ -50,20 +51,127 @@ function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onCom
}
});
+ // UTF-8解码函数,用于微信小程序真机环境
+ function utf8Decode(uint8Array) {
+ let result = '';
+ let i = 0;
+ const len = uint8Array.length;
+
+ while (i < len) {
+ const byte1 = uint8Array[i];
+
+ // 1字节字符 (0xxxxxxx)
+ if (byte1 < 0x80) {
+ result += String.fromCharCode(byte1);
+ i++;
+ }
+ // 2字节字符 (110xxxxx 10xxxxxx)
+ else if (byte1 >= 0xC0 && byte1 < 0xE0) {
+ if (i + 1 < len) {
+ const byte2 = uint8Array[i + 1];
+ if (byte2 >= 0x80 && byte2 < 0xC0) {
+ const codePoint = ((byte1 & 0x1F) << 6) | (byte2 & 0x3F);
+ result += String.fromCharCode(codePoint);
+ i += 2;
+ continue;
+ }
+ }
+ // 无效的UTF-8序列,跳过
+ result += '�';
+ i++;
+ }
+ // 3字节字符 (1110xxxx 10xxxxxx 10xxxxxx)
+ else if (byte1 >= 0xE0 && byte1 < 0xF0) {
+ if (i + 2 < len) {
+ const byte2 = uint8Array[i + 1];
+ const byte3 = uint8Array[i + 2];
+ if ((byte2 >= 0x80 && byte2 < 0xC0) && (byte3 >= 0x80 && byte3 < 0xC0)) {
+ const codePoint = ((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F);
+ result += String.fromCharCode(codePoint);
+ i += 3;
+ continue;
+ }
+ }
+ // 无效的UTF-8序列,跳过
+ result += '�';
+ i++;
+ }
+ // 4字节字符 (11110xxx 10xxxxxx 10xxxxxx 10xxxxxx)
+ else if (byte1 >= 0xF0 && byte1 < 0xF8) {
+ if (i + 3 < len) {
+ const byte2 = uint8Array[i + 1];
+ const byte3 = uint8Array[i + 2];
+ const byte4 = uint8Array[i + 3];
+ if ((byte2 >= 0x80 && byte2 < 0xC0) && (byte3 >= 0x80 && byte3 < 0xC0) && (byte4 >= 0x80 && byte4 < 0xC0)) {
+ let codePoint = ((byte1 & 0x07) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F);
+ // 处理UTF-16代理对
+ if (codePoint >= 0x10000) {
+ codePoint -= 0x10000;
+ const highSurrogate = (codePoint >> 10) + 0xD800;
+ const lowSurrogate = (codePoint & 0x3FF) + 0xDC00;
+ result += String.fromCharCode(highSurrogate, lowSurrogate);
+ } else {
+ result += String.fromCharCode(codePoint);
+ }
+ i += 4;
+ continue;
+ }
+ }
+ // 无效的UTF-8序列,跳过
+ result += '�';
+ i++;
+ }
+ // 无效的UTF-8序列,跳过
+ else {
+ result += '�';
+ i++;
+ }
+ }
+
+ return result;
+ }
+
// 监听分块数据
requestTask.onChunkReceived((res) => {
try {
- const decoder = new TextDecoder('utf-8');
- const chunk = decoder.decode(new Uint8Array(res.data));
+ // 微信小程序兼容处理:微信小程序不支持TextDecoder,使用自定义UTF-8解码
+ let chunk = '';
+ if (typeof TextDecoder !== 'undefined') {
+ // 支持TextDecoder的环境(如开发者工具)
+ const decoder = new TextDecoder('utf-8');
+ chunk = decoder.decode(new Uint8Array(res.data));
+ } else {
+ // 微信小程序真机环境,使用自定义UTF-8解码函数
+ const uint8Array = new Uint8Array(res.data);
+ chunk = utf8Decode(uint8Array);
+ }
+ console.log('📦 收到分块数据:', chunk);
buffer += chunk;
let lines = buffer.split("\n");
buffer = lines.pop() || ''; // 保留不完整的行
+ console.log('📝 解析到行:', lines.length, '行,缓冲区剩余:', buffer.length, '字符');
for (let line of lines) {
- if (line.startsWith("data: ")) {
- const jsonData = line.slice(6).trim();
+ console.log('🔍 处理行:', line);
+ // 处理重复的 data: 前缀
+ let processedLine = line;
+ // 移除所有开头的 data: 前缀(无论是否有空格),直到只剩下一个或没有
+ while (processedLine.startsWith("data:")) {
+ // 检查是否还有另一个 data: 前缀
+ const nextPart = processedLine.slice(5).trimStart();
+ if (nextPart.startsWith("data:")) {
+ processedLine = nextPart;
+ } else {
+ break;
+ }
+ }
+
+ if (processedLine.startsWith("data: ")) {
+ const jsonData = processedLine.slice(6).trim();
+ console.log('📄 提取的JSON数据:', jsonData);
if (jsonData === "[DONE]") {
+ console.log('✅ 收到结束标记 [DONE]');
onComplete && onComplete();
resolve();
return;
@@ -72,11 +180,35 @@ function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onCom
if (jsonData && jsonData.trim()) {
try {
const parsedData = JSON.parse(jsonData);
+ console.log('🔧 解析后的JSON:', parsedData);
+ // 检查是否有错误信息
+ const finishReason = parsedData?.choices?.[0]?.finish_reason;
+ if (finishReason === "error") {
+ let errorContent = parsedData?.choices?.[0]?.delta?.content || "流式请求失败";
+ console.error('❌ 收到错误信息:', errorContent);
+
+ // 优化token超限错误提示
+ if (errorContent.includes("maximum input ids length")) {
+ errorContent = "对话历史过长,请尝试清除部分历史记录或简化问题";
+ }
+
+ // 只有当未收到正常内容时才显示错误信息
+ if (!hasReceivedContent) {
+ // 显示错误信息给用户
+ uni.showToast({
+ title: errorContent,
+ icon: 'none',
+ duration: 3000
+ });
+ }
+ }
// 处理标准的choices格式
- if (parsedData?.choices?.[0]?.delta?.content) {
+ else if (parsedData?.choices?.[0]?.delta?.content) {
const content = parsedData.choices[0].delta.content;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(content):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -84,6 +216,8 @@ function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onCom
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
const content = parsedData.choices[0].delta.reasoning_content;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(reasoning_content):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -91,14 +225,42 @@ function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onCom
else if (parsedData?.tool?.response) {
const content = parsedData.tool.response;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(tool.response):', content);
onDataReceived && onDataReceived(content);
}
}
+ // 处理其他可能的内容格式
+ else if (parsedData?.content) {
+ // 直接返回content字段的情况
+ const content = parsedData.content;
+ if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(direct content):', content);
+ onDataReceived && onDataReceived(content);
+ }
+ }
+ // 处理完整的text字段(非流式)
+ else if (parsedData?.choices?.[0]?.text) {
+ const content = parsedData.choices[0].text;
+ if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(full text):', content);
+ onDataReceived && onDataReceived(content);
+ }
+ }
+ else {
+ console.warn('⚠️ 未匹配到任何内容格式:', parsedData);
+ }
} catch (e) {
- console.error("JSON 解析失败:", e.message);
+ console.error("JSON 解析失败:", e.message, "原始数据:", jsonData);
}
}
}
+ else if (processedLine.trim()) {
+ // 处理非data:开头的行
+ console.warn('⚠️ 收到非data:开头的行:', processedLine);
+ }
}
} catch (error) {
console.error('处理分块数据失败:', error);
@@ -135,6 +297,7 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
let buffer = "";
let retryCount = 0;
const maxRetries = 3;
+ let hasReceivedContent = false;
while (true) {
const {
@@ -157,9 +320,25 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
console.log(`📦 Processing ${lines.length} lines, buffer length: ${buffer.length}`);
for (let line of lines) {
- if (line.startsWith("data: ")) {
- const jsonData = line.slice(6).trim();
+ console.log('🔍 处理行:', line);
+ // 处理重复的 data: 前缀
+ let processedLine = line;
+ // 移除所有开头的 data: 前缀(无论是否有空格),直到只剩下一个或没有
+ while (processedLine.startsWith("data:")) {
+ // 检查是否还有另一个 data: 前缀
+ const nextPart = processedLine.slice(5).trimStart();
+ if (nextPart.startsWith("data:")) {
+ processedLine = nextPart;
+ } else {
+ break;
+ }
+ }
+
+ if (processedLine.startsWith("data: ")) {
+ const jsonData = processedLine.slice(6).trim();
+ console.log('📄 提取的JSON数据:', jsonData);
if (jsonData === "[DONE]") {
+ console.log('✅ 收到结束标记 [DONE]');
onComplete && onComplete();
resolve();
return;
@@ -169,11 +348,35 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
// 检查JSON数据是否完整
if (jsonData && jsonData.trim() && jsonData !== "[DONE]") {
const parsedData = JSON.parse(jsonData);
+ console.log('🔧 解析后的JSON:', parsedData);
+ // 检查是否有错误信息
+ const finishReason = parsedData?.choices?.[0]?.finish_reason;
+ if (finishReason === "error") {
+ let errorContent = parsedData?.choices?.[0]?.delta?.content || "流式请求失败";
+ console.error('❌ 收到错误信息:', errorContent);
+
+ // 优化token超限错误提示
+ if (errorContent.includes("maximum input ids length")) {
+ errorContent = "对话历史过长,请尝试清除部分历史记录或简化问题";
+ }
+
+ // 只有当未收到正常内容时才显示错误信息
+ if (!hasReceivedContent) {
+ // 显示错误信息给用户
+ uni.showToast({
+ title: errorContent,
+ icon: 'none',
+ duration: 3000
+ });
+ }
+ }
// 处理标准的choices格式
- if (parsedData?.choices?.[0]?.delta?.content) {
+ else if (parsedData?.choices?.[0]?.delta?.content) {
const content = parsedData.choices[0].delta.content;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(content):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -181,6 +384,8 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
const content = parsedData.choices[0].delta.reasoning_content;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(reasoning_content):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -188,6 +393,27 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
else if (parsedData?.tool?.response) {
const content = parsedData.tool.response;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(tool.response):', content);
+ onDataReceived && onDataReceived(content);
+ }
+ }
+ // 处理其他可能的内容格式
+ else if (parsedData?.content) {
+ // 直接返回content字段的情况
+ const content = parsedData.content;
+ if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(direct content):', content);
+ onDataReceived && onDataReceived(content);
+ }
+ }
+ // 处理完整的text字段(非流式)
+ else if (parsedData?.choices?.[0]?.text) {
+ const content = parsedData.choices[0].text;
+ if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(full text):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -201,6 +427,10 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
// 不抛出错误,继续处理下一个数据块
}
}
+ else if (processedLine.trim()) {
+ // 处理非data:开头的行
+ console.warn('⚠️ 收到非data:开头的行:', processedLine);
+ }
}
}
@@ -209,16 +439,55 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
console.log("📦 Processing remaining buffer:", buffer.substring(0, 100) + "...");
const lines = buffer.split("\n");
for (let line of lines) {
- if (line.startsWith("data: ")) {
- const jsonData = line.slice(6).trim();
+ console.log('🔍 处理剩余缓冲区行:', line);
+ // 处理重复的 data: 前缀
+ let processedLine = line;
+ // 移除所有开头的 data: 前缀(无论是否有空格),直到只剩下一个或没有
+ while (processedLine.startsWith("data:")) {
+ // 检查是否还有另一个 data: 前缀
+ const nextPart = processedLine.slice(5).trimStart();
+ if (nextPart.startsWith("data:")) {
+ processedLine = nextPart;
+ } else {
+ break;
+ }
+ }
+
+ if (processedLine.startsWith("data: ")) {
+ const jsonData = processedLine.slice(6).trim();
+ console.log('📄 提取的剩余JSON数据:', jsonData);
if (jsonData && jsonData !== "[DONE]") {
try {
const parsedData = JSON.parse(jsonData);
+ console.log('🔧 解析后的剩余JSON:', parsedData);
+ // 检查是否有错误信息
+ const finishReason = parsedData?.choices?.[0]?.finish_reason;
+ if (finishReason === "error") {
+ let errorContent = parsedData?.choices?.[0]?.delta?.content || "流式请求失败";
+ console.error('❌ 收到错误信息:', errorContent);
+
+ // 优化token超限错误提示
+ if (errorContent.includes("maximum input ids length")) {
+ errorContent = "对话历史过长,请尝试清除部分历史记录或简化问题";
+ }
+
+ // 只有当未收到正常内容时才显示错误信息
+ if (!hasReceivedContent) {
+ // 显示错误信息给用户
+ uni.showToast({
+ title: errorContent,
+ icon: 'none',
+ duration: 3000
+ });
+ }
+ }
// 处理标准的choices格式
- if (parsedData?.choices?.[0]?.delta?.content) {
+ else if (parsedData?.choices?.[0]?.delta?.content) {
const content = parsedData.choices[0].delta.content;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(content):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -226,6 +495,8 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
else if (parsedData?.choices?.[0]?.delta?.reasoning_content) {
const content = parsedData.choices[0].delta.reasoning_content;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(reasoning_content):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -233,6 +504,27 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
else if (parsedData?.tool?.response) {
const content = parsedData.tool.response;
if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(tool.response):', content);
+ onDataReceived && onDataReceived(content);
+ }
+ }
+ // 处理其他可能的内容格式
+ else if (parsedData?.content) {
+ // 直接返回content字段的情况
+ const content = parsedData.content;
+ if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(direct content):', content);
+ onDataReceived && onDataReceived(content);
+ }
+ }
+ // 处理完整的text字段(非流式)
+ else if (parsedData?.choices?.[0]?.text) {
+ const content = parsedData.choices[0].text;
+ if (content) {
+ hasReceivedContent = true;
+ console.log('📤 调用onDataReceived(full text):', content);
onDataReceived && onDataReceived(content);
}
}
@@ -241,6 +533,10 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
}
}
}
+ else if (processedLine.trim()) {
+ // 处理非data:开头的行
+ console.warn('⚠️ 收到非data:开头的剩余行:', processedLine);
+ }
}
}
@@ -295,16 +591,27 @@ export function chatRequest(url, data = {}, method = 'GET', loading = false, hea
return
}
uni.showToast({
- title: msg,
+ title: msg || '请求失败',
icon: 'none'
})
+ // 拒绝Promise并提供详细错误信息
+ const err = new Error(msg || '请求失败,服务器返回错误码: ' + code)
+ err.error = resData
+ reject(err)
+ } else {
+ // 处理非200状态码
+ const errorMsg = `请求失败,HTTP状态码: ${resData.statusCode}`
+ uni.showToast({
+ title: errorMsg,
+ icon: 'none'
+ })
+ const err = new Error(errorMsg)
+ err.error = resData
+ reject(err)
}
if (resData.data?.code === 401 || resData.data?.code === 402) {
useUserStore().logOut()
}
- const err = new Error('请求出现异常,请联系工作人员')
- err.error = resData
- reject(err)
},
fail: (err) => {
reject(err)
diff --git a/utilsRc/config.js b/utilsRc/config.js
index 64eccf5..8813e80 100644
--- a/utilsRc/config.js
+++ b/utilsRc/config.js
@@ -11,6 +11,7 @@ let exports = {
// ========== baseUrl 配置方式选择 ==========
// 方式1:硬编码baseUrl(main分支使用,合并到main时不会影响现有功能)
baseUrl: 'https://www.xjksly.cn/sdrc-api', // 正式环境在济南人才上部署(不要轻易连接)
+ // baseUrl: 'http://cffe7966.natappfree.cc', // 正式环境在济南人才上部署(不要轻易连接)
// baseUrl: 'http://10.160.0.5:8907', // 开发环境
// baseUrl: 'http://172.20.1.48:8903', // 开发环境