地区选择开发,AI接口联调
This commit is contained in:
10
config.js
10
config.js
@@ -6,9 +6,9 @@
|
||||
*/
|
||||
export default {
|
||||
// baseUrl: 'http://39.98.44.136:8080', // 测试
|
||||
baseUrl: 'https://www.xjksly.cn/api/ks', // 测试
|
||||
// baseUrl: 'https://www.xjksly.cn/api/ks', // 测试
|
||||
// baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试
|
||||
// baseUrl: 'https://www.xjksly.cn/api/ks', // 测试
|
||||
baseUrl: 'http://ks.zhaopinzao8dian.com/api/ks', // 测试
|
||||
|
||||
// LCBaseUrl:'http://10.110.145.145:9100',//内网端口
|
||||
// LCBaseUrlInner:'http://10.110.145.145:10100',//招聘、培训、帮扶
|
||||
@@ -20,11 +20,11 @@ export default {
|
||||
trainVideoImgUrl:'https://www.xjksly.cn/prod-api/file/file/minio',
|
||||
// sseAI+
|
||||
// StreamBaseURl: 'http://39.98.44.136:8000',
|
||||
StreamBaseURl: 'https://qd.zhaopinzao8dian.com/ai',
|
||||
StreamBaseURl: 'https://www.xjksly.cn/api/ks/app/chat',
|
||||
// StreamBaseURl: 'https://qd.zhaopinzao8dian.com/ai/test',
|
||||
// 语音转文字
|
||||
// vioceBaseURl: 'ws://39.98.44.136:8080/speech-recognition',
|
||||
vioceBaseURl: 'wss://qd.zhaopinzao8dian.com/api/speech-recognition',
|
||||
vioceBaseURl: 'wss://qd.zhaopinzao8dian.com/api/ks/app/speech/asr',
|
||||
// vioceBaseURl: 'wss://qd.zhaopinzao8dian.com/api/speech-recognition',
|
||||
// 语音合成
|
||||
speechSynthesis: 'wss://qd.zhaopinzao8dian.com/api/speech-synthesis',
|
||||
// indexedDB
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
"navigationBarTitleText": "喀什智慧就业平台"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/city-select/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "选择城市"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/mine/mine",
|
||||
"style": {
|
||||
|
||||
@@ -551,7 +551,9 @@ const scrollToBottom = throttle(function () {
|
||||
}, 500);
|
||||
|
||||
function getGuess() {
|
||||
$api.chatRequest('/guest', { sessionId: chatSessionID.value }, 'POST').then((res) => {
|
||||
// $api.chatRequest('/guest', { sessionId: chatSessionID.value }, 'POST').then((res) => {
|
||||
$api.chatRequest('/guest', undefined, 'POST').then((res) => {
|
||||
console.log('getGuess ---- res:', res);
|
||||
guessList.value = res.data;
|
||||
showGuess.value = true;
|
||||
nextTick(() => {
|
||||
@@ -804,22 +806,22 @@ function refreshMarkdown(index) {
|
||||
}
|
||||
|
||||
const jobSearchQueries = [
|
||||
'青岛有哪些薪资 12K 以上的岗位适合我?',
|
||||
'青岛 3 年工作经验能找到哪些 12K 以上的工作?',
|
||||
'青岛哪些公司在招聘,薪资范围在 12K 以上?',
|
||||
'青岛有哪些企业提供 15K 以上的岗位?',
|
||||
'青岛哪些公司在招 3-5 年经验的岗位?',
|
||||
'我有三年的工作经验,能否推荐一些适合我的青岛的国企 岗位?',
|
||||
'青岛国企目前在招聘哪些岗位?',
|
||||
'青岛有哪些适合 3 年经验的国企岗位?',
|
||||
'青岛国企招聘的岗位待遇如何?',
|
||||
'青岛国企岗位的薪资水平是多少?',
|
||||
'青岛哪些国企支持双休 & 五险一金完善?',
|
||||
'青岛有哪些公司支持远程办公?',
|
||||
'青岛有哪些外企的岗位,薪资 12K 以上的多吗?',
|
||||
'青岛哪些企业在招聘 Web3.0 相关岗位?',
|
||||
'青岛哪些岗位支持海外远程?薪资如何?',
|
||||
'青岛招聘 AI/大数据相关岗位的公司有哪些?',
|
||||
'喀什地区有哪些薪资 12K 以上的岗位适合我?',
|
||||
'喀什地区 3 年工作经验能找到哪些 12K 以上的工作?',
|
||||
'喀什地区哪些公司在招聘,薪资范围在 12K 以上?',
|
||||
'喀什地区有哪些企业提供 15K 以上的岗位?',
|
||||
'喀什地区哪些公司在招 3-5 年经验的岗位?',
|
||||
'我有三年的工作经验,能否推荐一些适合我的喀什地区的国企 岗位?',
|
||||
'喀什地区国企目前在招聘哪些岗位?',
|
||||
'喀什地区有哪些适合 3 年经验的国企岗位?',
|
||||
'喀什地区国企招聘的岗位待遇如何?',
|
||||
'喀什地区国企岗位的薪资水平是多少?',
|
||||
'喀什地区哪些国企支持双休 & 五险一金完善?',
|
||||
'喀什地区有哪些公司支持远程办公?',
|
||||
'喀什地区有哪些外企的岗位,薪资 12K 以上的多吗?',
|
||||
'喀什地区哪些企业在招聘 Web3.0 相关岗位?',
|
||||
'喀什地区哪些岗位支持海外远程?薪资如何?',
|
||||
'喀什地区招聘 AI/大数据相关岗位的公司有哪些?',
|
||||
];
|
||||
|
||||
function changeQueries(value) {
|
||||
|
||||
425
pages/city-select/index.vue
Normal file
425
pages/city-select/index.vue
Normal file
@@ -0,0 +1,425 @@
|
||||
<template>
|
||||
<view class="city-select-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<!-- <view class="nav-bar">
|
||||
<view class="nav-left" @click="navBack">
|
||||
<uni-icons type="left" size="24" color="#333"></uni-icons>
|
||||
</view>
|
||||
<view class="nav-title">选择城市</view>
|
||||
<view class="nav-right"></view>
|
||||
</view> -->
|
||||
|
||||
<!-- 搜索框 -->
|
||||
<view class="search-box">
|
||||
<uni-icons class="search-icon" type="search" size="20" color="#999"></uni-icons>
|
||||
<input
|
||||
class="search-input"
|
||||
placeholder="搜索城市名/拼音"
|
||||
placeholder-style="color: #999"
|
||||
v-model="searchText"
|
||||
@input="handleSearch"
|
||||
>
|
||||
</view>
|
||||
|
||||
<!-- 定位城市 -->
|
||||
<!-- <view class="location-section">
|
||||
<view class="section-title">定位城市</view>
|
||||
<view class="location-city" :class="{ active: selectedCity.code === locationCity.code }" @click="selectCity(locationCity)">
|
||||
{{ locationCity.name }}
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 热门城市 -->
|
||||
<!-- <view class="hot-section">
|
||||
<view class="section-title">热门城市</view>
|
||||
<view class="hot-cities">
|
||||
<view
|
||||
class="city-item"
|
||||
:class="{ active: selectedCity.code === item.code }"
|
||||
v-for="item in hotCities"
|
||||
:key="item.code"
|
||||
@click="selectCity(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
|
||||
<!-- 城市数量统计 -->
|
||||
<!-- <view class="city-count" v-if="allCities.length > 0">
|
||||
共找到 {{ allCities.length }} 个城市
|
||||
</view> -->
|
||||
|
||||
<!-- 城市列表 -->
|
||||
<view class="city-list-section">
|
||||
<scroll-view
|
||||
class="city-list-content"
|
||||
scroll-y
|
||||
:scroll-into-view="currentScrollId"
|
||||
:scroll-with-animation="true"
|
||||
>
|
||||
<view
|
||||
class="city-group"
|
||||
v-for="group in cityGroups"
|
||||
:key="group.letter"
|
||||
:id="`city-group-${group.letter}`"
|
||||
>
|
||||
<view class="group-title">{{ group.letter }}</view>
|
||||
<view
|
||||
class="city-item"
|
||||
:class="{ active: selectedCity.code === item.code }"
|
||||
v-for="item in group.cities"
|
||||
:key="item.code"
|
||||
@click="selectCity(item)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 右侧字母索引 -->
|
||||
<view class="letter-index">
|
||||
<view
|
||||
class="letter-item"
|
||||
v-for="letter in letters"
|
||||
:key="letter"
|
||||
@click="scrollToLetter(letter)"
|
||||
:class="{ active: currentLetter === letter }"
|
||||
>
|
||||
{{ letter }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, inject } from 'vue';
|
||||
const { $api, navTo } = inject('globalFunction');
|
||||
|
||||
// 搜索文本
|
||||
const searchText = ref('');
|
||||
// 定位城市
|
||||
const locationCity = ref({ code: '110100', name: '北京' });
|
||||
// 选中的城市
|
||||
const selectedCity = ref({ code: '', name: '' });
|
||||
// 当前显示的字母
|
||||
const currentLetter = ref('');
|
||||
// 当前滚动到的城市组ID
|
||||
const currentScrollId = ref('');
|
||||
// 城市数据
|
||||
const allCities = ref([]);
|
||||
// 字母列表
|
||||
const letters = ref(['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']);
|
||||
|
||||
|
||||
// 按字母分组的城市
|
||||
const cityGroups = computed(() => {
|
||||
const groups = {};
|
||||
const filteredCities = allCities.value.filter(city => {
|
||||
if (!searchText.value) return true;
|
||||
return city.name.includes(searchText.value) || city.pinyin?.includes(searchText.value.toUpperCase());
|
||||
});
|
||||
|
||||
console.log('过滤后用于分组的城市:', filteredCities);
|
||||
|
||||
// 初始化字母分组
|
||||
letters.value.forEach(letter => {
|
||||
groups[letter] = { letter, cities: [] };
|
||||
});
|
||||
|
||||
// 将城市分配到对应字母组
|
||||
filteredCities.forEach(city => {
|
||||
const firstLetter = city.pinyin || '#';
|
||||
// 如果字母不在预定义列表中,将其添加到分组中
|
||||
if (!groups[firstLetter]) {
|
||||
groups[firstLetter] = { letter: firstLetter, cities: [] };
|
||||
}
|
||||
groups[firstLetter].cities.push(city);
|
||||
});
|
||||
|
||||
const result = Object.values(groups).filter(group => group.cities.length > 0);
|
||||
|
||||
// 对城市组进行排序
|
||||
result.sort((a, b) => {
|
||||
// '#' 符号应该排在最前面
|
||||
if (a.letter === '#') return -1;
|
||||
if (b.letter === '#') return 1;
|
||||
// 其他字母按字母顺序排序
|
||||
return a.letter.localeCompare(b.letter);
|
||||
});
|
||||
|
||||
console.log('最终分组结果:', result);
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
// 获取城市数据
|
||||
const getCityData = async () => {
|
||||
try {
|
||||
// 直接获取所有城市数据,新接口已经返回了所有层级的数据
|
||||
const res = await $api.createRequest('/cms/dict/sysarea/listCity', {});
|
||||
console.log('城市数据接口返回:', res);
|
||||
if (res.code === 200 && res.data) {
|
||||
console.log('原始城市数据:', res.data);
|
||||
// 显示接口返回的所有城市数据
|
||||
const filteredCities = res.data;
|
||||
console.log('过滤后城市数据:', filteredCities);
|
||||
|
||||
// 直接使用后端返回的zm字段作为拼音首字母,并转换为大写
|
||||
allCities.value = filteredCities.map(city => ({
|
||||
...city,
|
||||
pinyin: (city.zm || '#').toUpperCase()
|
||||
}));
|
||||
|
||||
console.log('使用后端zm字段的城市数据:', allCities.value);
|
||||
|
||||
// 按拼音首字母排序
|
||||
allCities.value.sort((a, b) => {
|
||||
// 首先按拼音首字母排序
|
||||
if (a.pinyin !== b.pinyin) {
|
||||
// '#' 符号应该排在最前面
|
||||
if (a.pinyin === '#') return -1;
|
||||
if (b.pinyin === '#') return 1;
|
||||
return a.pinyin.localeCompare(b.pinyin);
|
||||
}
|
||||
// 首字母相同时,按城市名称排序
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取城市数据失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取拼音首字母(使用更准确的映射表)
|
||||
const getPinyinFirstLetter = (name) => {
|
||||
const firstChar = name.charAt(0);
|
||||
|
||||
// 查找对应的拼音首字母
|
||||
for (const [letter, chars] of Object.entries(pinyinMap)) {
|
||||
if (chars.includes(firstChar)) {
|
||||
return letter;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到,返回#
|
||||
return '#';
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = () => {
|
||||
// 搜索逻辑已在cityGroups计算属性中处理
|
||||
};
|
||||
|
||||
// 选择城市
|
||||
const selectCity = (city) => {
|
||||
selectedCity.value = city;
|
||||
// 返回上一页并传递选择的城市
|
||||
uni.navigateBack({
|
||||
delta: 1,
|
||||
success: () => {
|
||||
// 发送事件通知首页选择了城市
|
||||
uni.$emit('citySelected', city);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 滚动到指定字母
|
||||
const scrollToLetter = (letter) => {
|
||||
currentLetter.value = letter;
|
||||
// 更新滚动ID,触发scroll-view滚动
|
||||
currentScrollId.value = `city-group-${letter}`;
|
||||
};
|
||||
|
||||
// 返回上一页
|
||||
const navBack = () => {
|
||||
uni.navigateBack({ delta: 1 });
|
||||
};
|
||||
|
||||
// 组件挂载时获取城市数据
|
||||
onMounted(() => {
|
||||
getCityData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.city-select-container {
|
||||
background-color: #f5f5f5;
|
||||
min-height: 100vh;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
/* 导航栏 */
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 44px;
|
||||
padding: 0 16px;
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
}
|
||||
|
||||
.nav-left,
|
||||
.nav-right {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 搜索框 */
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 12px 16px;
|
||||
padding:8px 16px;
|
||||
background-color: #fff;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.search-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
border: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* 定位城市 */
|
||||
.location-section {
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.location-city {
|
||||
display: inline-block;
|
||||
padding: 8px 20px;
|
||||
background-color: #f0f9ff;
|
||||
color: #007aff;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.location-city.active {
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 热门城市 */
|
||||
.hot-section {
|
||||
padding: 16px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.city-count {
|
||||
padding: 0 16px 12px;
|
||||
background-color: #fff;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.hot-cities {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.city-item {
|
||||
padding: 8px 20px;
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.city-item.active {
|
||||
background-color: #007aff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 城市列表 */
|
||||
.city-list-section {
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
height: calc(100vh - 180px);
|
||||
}
|
||||
|
||||
.city-list-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.city-group {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin: 12px 0 8px 0;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.city-list-content .city-item {
|
||||
display: block;
|
||||
padding: 12px 4px;
|
||||
margin-bottom: 0;
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.city-list-content .city-item.active {
|
||||
background-color: transparent;
|
||||
color: #007aff;
|
||||
}
|
||||
|
||||
/* 右侧字母索引 */
|
||||
.letter-index {
|
||||
width: 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.letter-item {
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.letter-item.active {
|
||||
color: #007aff;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
@@ -250,10 +250,16 @@
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="jobs-add button-click" @click="navTo('/packageA/pages/addPosition/addPosition')">
|
||||
<uni-icons class="iconsearch" color="#666D7F" type="plusempty" size="18"></uni-icons>
|
||||
<text>添加</text>
|
||||
</view>
|
||||
<view class="jobs-add button-click" @click="navTo('/pages/city-select/index')" style="padding-right:0;">
|
||||
<!-- <uni-icons class="iconsearch" color="#666D7F" type="plusempty" size="18"></uni-icons> -->
|
||||
<text>{{ selectedCity.name || '地区' }}</text>
|
||||
<image class="right-sx" :class="{ active: showFilter }" src="@/static/icon/shaixun.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="filter-bottom">
|
||||
<view class="btm-left">
|
||||
@@ -317,7 +323,7 @@
|
||||
<view class="falls-card-company" v-show="isShowJw !== 3">
|
||||
{{ config.appInfo.areaName }}
|
||||
<!-- {{ job.jobLocation }} -->
|
||||
<dict-Label dictType="area" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
<dict-Label dictType="jobLocationAreaCode" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
</view>
|
||||
<view class="falls-card-pepleNumber">
|
||||
<view>
|
||||
@@ -394,7 +400,7 @@
|
||||
<view class="falls-card-company" v-show="isShowJw !== 3">
|
||||
{{ config.appInfo.areaName }}
|
||||
<!-- {{ job.jobLocation }} -->
|
||||
<dict-Label dictType="area" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
<dict-Label dictType="jobLocationAreaCode" :value="job.jobLocationAreaCode"></dict-Label>
|
||||
</view>
|
||||
<view class="falls-card-pepleNumber">
|
||||
<view>
|
||||
@@ -631,6 +637,8 @@ const inputText = ref('');
|
||||
const showFilter = ref(false);
|
||||
const selectFilterModel = ref(null);
|
||||
const showModel = ref(false);
|
||||
// 选中的城市
|
||||
const selectedCity = ref({ code: '', name: '' });
|
||||
const rangeOptions = ref([
|
||||
{ value: 0, text: '推荐' },
|
||||
{ value: 1, text: '最热' },
|
||||
@@ -723,6 +731,7 @@ onMounted(() => {
|
||||
// 在组件挂载时绑定事件监听,确保只绑定一次
|
||||
// 先移除可能存在的旧监听,避免重复绑定
|
||||
uni.$off('showLoginModal');
|
||||
uni.$off('citySelected');
|
||||
|
||||
// 绑定新的监听
|
||||
uni.$on('showLoginModal', () => {
|
||||
@@ -731,6 +740,15 @@ onMounted(() => {
|
||||
pageNull.value = 0;
|
||||
});
|
||||
|
||||
// 监听城市选择事件
|
||||
uni.$on('citySelected', (city) => {
|
||||
console.log('收到citySelected事件,选择的城市:', city);
|
||||
selectedCity.value = city;
|
||||
// 可以在这里添加根据城市筛选职位的逻辑
|
||||
conditionSearch.value.jobLocationAreaCode = city.code;
|
||||
getJobRecommend('refresh');
|
||||
});
|
||||
|
||||
// 获取企业信息
|
||||
getCompanyInfo();
|
||||
});
|
||||
@@ -738,6 +756,7 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
// 组件销毁时移除事件监听
|
||||
uni.$off('showLoginModal');
|
||||
uni.$off('citySelected');
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
@@ -1728,6 +1747,9 @@ defineExpose({ loadData });
|
||||
min-width: 80rpx;
|
||||
padding: 8rpx 12rpx;
|
||||
white-space: nowrap;
|
||||
.right-sx
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
.filter-bottom
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
|
||||
@@ -113,6 +113,12 @@ 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,
|
||||
|
||||
@@ -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,
|
||||
@@ -55,15 +56,33 @@ function StreamRequestMiniProgram(url, data = {}, onDataReceived, onError, onCom
|
||||
try {
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
const chunk = decoder.decode(new Uint8Array(res.data));
|
||||
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 +91,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 +127,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 +136,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 +208,7 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
|
||||
let buffer = "";
|
||||
let retryCount = 0;
|
||||
const maxRetries = 3;
|
||||
let hasReceivedContent = false;
|
||||
|
||||
while (true) {
|
||||
const {
|
||||
@@ -157,9 +231,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 +259,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 +295,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 +304,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 +338,10 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
|
||||
// 不抛出错误,继续处理下一个数据块
|
||||
}
|
||||
}
|
||||
else if (processedLine.trim()) {
|
||||
// 处理非data:开头的行
|
||||
console.warn('⚠️ 收到非data:开头的行:', processedLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,16 +350,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 +406,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 +415,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 +444,10 @@ function StreamRequestH5(url, data = {}, onDataReceived, onError, onComplete) {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (processedLine.trim()) {
|
||||
// 处理非data:开头的行
|
||||
console.warn('⚠️ 收到非data:开头的剩余行:', processedLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,16 +502,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)
|
||||
|
||||
Reference in New Issue
Block a user