init
This commit is contained in:
73
stores/BaseDBStore.js
Normal file
73
stores/BaseDBStore.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// BaseStore.js - 基础Store类
|
||||
import IndexedDBHelper from '@/common/IndexedDBHelper.js'
|
||||
// import UniStorageHelper from '../common/UniStorageHelper'
|
||||
import useChatGroupDBStore from './userChatGroupStore'
|
||||
import config from '@/config'
|
||||
|
||||
|
||||
class BaseStore {
|
||||
db = null
|
||||
isDBReady = false
|
||||
dbName = 'BrowsingHistory'
|
||||
constructor() {
|
||||
this.checkAndInitDB()
|
||||
}
|
||||
checkAndInitDB() {
|
||||
// 获取本地数据库版本
|
||||
if (config.OnlyUseCachedDB) {
|
||||
return this.initDB()
|
||||
}
|
||||
const localVersion = uni.getStorageSync('indexedDBVersion') || 1
|
||||
console.log('DBVersion: ', localVersion, config.DBversion)
|
||||
if (localVersion === config.DBversion) {
|
||||
this.initDB()
|
||||
} else {
|
||||
console.log('清空本地数据库')
|
||||
this.clearDB().then(() => {
|
||||
uni.setStorageSync('indexedDBVersion', config.DBversion);
|
||||
this.initDB();
|
||||
});
|
||||
}
|
||||
}
|
||||
initDB() {
|
||||
// // #ifdef H5
|
||||
this.db = new IndexedDBHelper(this.dbName, config.DBversion);
|
||||
// // #endif
|
||||
// // #ifndef H5
|
||||
// this.db = new UniStorageHelper(this.dbName, config.DBversion);
|
||||
// // #endif
|
||||
this.db.openDB([{
|
||||
name: 'record',
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
}, {
|
||||
name: 'messageGroup',
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
}, {
|
||||
name: 'messages',
|
||||
keyPath: "id",
|
||||
autoIncrement: true,
|
||||
indexes: [{
|
||||
name: 'parentGroupId',
|
||||
key: 'parentGroupId',
|
||||
unique: false
|
||||
}]
|
||||
}]).then(async () => {
|
||||
useChatGroupDBStore().init()
|
||||
this.isDBReady = true
|
||||
});
|
||||
}
|
||||
async clearDB() {
|
||||
return new Promise((resolve, rejetc) => {
|
||||
new IndexedDBHelper().deleteDB(this.dbName).then(() => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const baseDB = new BaseStore()
|
||||
|
||||
export default baseDB
|
||||
188
stores/useDictStore.js
Normal file
188
stores/useDictStore.js
Normal file
@@ -0,0 +1,188 @@
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia';
|
||||
import {
|
||||
reactive,
|
||||
ref,
|
||||
} from 'vue'
|
||||
import {
|
||||
createRequest
|
||||
} from "../utils/request";
|
||||
|
||||
// 静态树 O(1) 超快查询!!!!!
|
||||
let IndustryMap = null
|
||||
// 构建索引
|
||||
function buildIndex(tree) {
|
||||
const map = new Map();
|
||||
|
||||
function traverse(nodes) {
|
||||
for (const node of nodes) {
|
||||
map.set(node.id, node);
|
||||
if (node.children && node.children.length) {
|
||||
traverse(node.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
traverse(tree);
|
||||
return map;
|
||||
}
|
||||
const useDictStore = defineStore("dict", () => {
|
||||
// 定义状态
|
||||
const complete = ref(false)
|
||||
const state = reactive({
|
||||
education: [],
|
||||
experience: [],
|
||||
area: [],
|
||||
scale: [],
|
||||
isPublish: [],
|
||||
sex: [],
|
||||
affiliation: [],
|
||||
industry: [],
|
||||
nature: [],
|
||||
noticeType: []
|
||||
})
|
||||
// political_affiliation
|
||||
const getDictData = async (dictType, dictName) => {
|
||||
try {
|
||||
if (dictType && dictName) {
|
||||
return getDictSelectOption(dictType).then((data) => {
|
||||
state[dictName] = data
|
||||
return data
|
||||
})
|
||||
}
|
||||
const [education, experience, area, scale, sex, affiliation, nature, noticeType] =
|
||||
await Promise.all([
|
||||
getDictSelectOption('education'),
|
||||
getDictSelectOption('experience'),
|
||||
getDictSelectOption('area', true),
|
||||
getDictSelectOption('scale'),
|
||||
getDictSelectOption('app_sex'),
|
||||
getDictSelectOption('political_affiliation'),
|
||||
getDictSelectOption('company_nature'),
|
||||
getDictSelectOption('sys_notice_type'),
|
||||
]);
|
||||
|
||||
state.education = education;
|
||||
state.experience = experience;
|
||||
state.area = area;
|
||||
state.scale = scale;
|
||||
state.sex = sex;
|
||||
state.affiliation = affiliation;
|
||||
state.nature = nature
|
||||
state.noticeType = noticeType
|
||||
complete.value = true
|
||||
getIndustryDict() // 获取行业
|
||||
} catch (error) {
|
||||
console.error('Error fetching dictionary data:', error);
|
||||
}
|
||||
};
|
||||
|
||||
async function getIndustryDict() {
|
||||
if (state.industry.length) return
|
||||
const resp = await createRequest(`/app/common/industry/treeselect`);
|
||||
if (resp.code === 200 && resp.data) {
|
||||
state.industry = resp.data
|
||||
IndustryMap = buildIndex(resp.data);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function industryLabel(dictType, value) {
|
||||
switch (dictType) {
|
||||
case 'industry':
|
||||
if (!IndustryMap) return
|
||||
const data = IndustryMap.get(Number(value))?.label || ''
|
||||
return data
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function dictLabel(dictType, value) {
|
||||
if (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 getTransformChildren(dictType, title = '', key = '') {
|
||||
if (dictType) {
|
||||
return {
|
||||
label: title,
|
||||
key: key || dictType,
|
||||
options: state[dictType],
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
async function getDictSelectOption(dictType, isDigital) {
|
||||
const resp = await createRequest(`/app/common/dict/${dictType}`);
|
||||
if (resp.code === 200 && resp.data) {
|
||||
const options = resp.data.map((item) => {
|
||||
return {
|
||||
text: item.dictLabel,
|
||||
label: item.dictLabel,
|
||||
value: isDigital ? Number(item.dictValue) : item.dictValue,
|
||||
key: item.dictCode,
|
||||
listClass: item.listClass,
|
||||
status: item.listClass,
|
||||
};
|
||||
});
|
||||
return options;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async function getDictValueEnum(dictType, isDigital) {
|
||||
const resp = await createRequest(`/app/common/dict/${dictType}`);
|
||||
if (resp.code === 200 && resp.data) {
|
||||
const opts = {};
|
||||
resp.data.forEach((item) => {
|
||||
opts[item.dictValue] = {
|
||||
text: item.dictLabel,
|
||||
label: item.dictLabel,
|
||||
value: isDigital ? Number(item.dictValue) : item.dictValue,
|
||||
key: item.dictCode,
|
||||
listClass: item.listClass,
|
||||
status: item.listClass,
|
||||
};
|
||||
});
|
||||
return opts;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// 导入
|
||||
return {
|
||||
getDictData,
|
||||
dictLabel,
|
||||
oneDictData,
|
||||
complete,
|
||||
getDictSelectOption,
|
||||
getTransformChildren,
|
||||
industryLabel
|
||||
}
|
||||
})
|
||||
|
||||
export default useDictStore;
|
||||
79
stores/useLocationStore.js
Normal file
79
stores/useLocationStore.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia';
|
||||
import {
|
||||
ref
|
||||
} from 'vue'
|
||||
import {
|
||||
msg
|
||||
} from '@/common/globalFunction.js'
|
||||
import config from '../config';
|
||||
|
||||
const useLocationStore = defineStore("location", () => {
|
||||
// 定义状态
|
||||
const longitudeVal = ref(null) // 经度
|
||||
const latitudeVal = ref(null) //纬度
|
||||
|
||||
function getLocation() {
|
||||
return new Promise((resole, reject) => {
|
||||
uni.getLocation({
|
||||
type: 'wgs84',
|
||||
altitude: true,
|
||||
isHighAccuracy: true,
|
||||
enableHighAccuracy: true, // 关键参数:启用传感器辅助
|
||||
timeout: 10000,
|
||||
success: function(res) {
|
||||
const resd = {
|
||||
longitude: 120.382665,
|
||||
latitude: 36.066938
|
||||
}
|
||||
if (config.UsingSimulatedPositioning) { // 使用模拟定位
|
||||
longitudeVal.value = resd.longitude
|
||||
latitudeVal.value = resd.latitude
|
||||
msg('用户位置获取成功')
|
||||
resole(resd)
|
||||
} else {
|
||||
longitudeVal.value = res.longitude
|
||||
latitudeVal.value = res.latitude
|
||||
msg('用户位置获取成功')
|
||||
resole(res)
|
||||
}
|
||||
},
|
||||
fail: function(err) {
|
||||
// longitudeVal.value = ''
|
||||
// latitudeVal.value = ''
|
||||
// reject(err)
|
||||
const resd = {
|
||||
longitude: 120.382665,
|
||||
latitude: 36.066938
|
||||
}
|
||||
longitudeVal.value = resd.longitude
|
||||
latitudeVal.value = resd.latitude
|
||||
msg('用户位置获取失败,使用模拟定位')
|
||||
resole(resd)
|
||||
},
|
||||
complete: function(e) {
|
||||
console.warn('getUserLocation' + JSON.stringify(e))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function longitude() {
|
||||
return longitudeVal.value
|
||||
}
|
||||
|
||||
function latitude() {
|
||||
return latitudeVal.value
|
||||
}
|
||||
|
||||
// 导入
|
||||
return {
|
||||
getLocation,
|
||||
longitudeVal,
|
||||
latitudeVal
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
export default useLocationStore;
|
||||
111
stores/useReadMsg.js
Normal file
111
stores/useReadMsg.js
Normal file
@@ -0,0 +1,111 @@
|
||||
// store/useReadMsg.js
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia'
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
watch
|
||||
} from 'vue'
|
||||
import {
|
||||
msg,
|
||||
$api,
|
||||
} from '../common/globalFunction';
|
||||
|
||||
// 控制消息
|
||||
export const useReadMsg = defineStore('readMsg', () => {
|
||||
const msgList = ref([])
|
||||
const badges = ref([{
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
count: 0
|
||||
},
|
||||
{
|
||||
count: 0
|
||||
},
|
||||
])
|
||||
|
||||
// 计算总未读数量,基于 notReadCount 字段
|
||||
const unreadCount = computed(() =>
|
||||
msgList.value.reduce((sum, msg) => sum + (msg.notReadCount || 0), 0)
|
||||
)
|
||||
|
||||
// 未读消息列表
|
||||
const unreadMsgList = computed(() =>
|
||||
msgList.value.filter(msg => msg.notReadCount > 0)
|
||||
)
|
||||
|
||||
|
||||
// 设置 TabBar 角标
|
||||
function updateTabBarBadge() {
|
||||
const count = unreadCount.value
|
||||
const index = 3
|
||||
const countVal = count > 99 ? '99+' : String(count)
|
||||
if (count === 0) {
|
||||
uni.removeTabBarBadge({
|
||||
index
|
||||
}) // 替换为你消息页面的 TabBar index
|
||||
badges.value[index] = {
|
||||
count: 0
|
||||
}
|
||||
} else {
|
||||
badges.value[index] = {
|
||||
count: countVal
|
||||
}
|
||||
uni.setTabBarBadge({
|
||||
index,
|
||||
text: countVal
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 拉取消息列表
|
||||
async function fetchMessages() {
|
||||
try {
|
||||
$api.createRequest('/app/notice/info', {
|
||||
isRead: 1
|
||||
}, "GET").then((res) => {
|
||||
msgList.value = res.data || []
|
||||
updateTabBarBadge()
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('获取消息失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
// 设置为已读
|
||||
async function markAsRead(item, index) {
|
||||
const msg = msgList.value[index]
|
||||
if (!msg || msg.isRead === 1) return
|
||||
|
||||
try {
|
||||
let params = {
|
||||
id: msg.noticeId
|
||||
}
|
||||
$api.createRequest('/app/notice/read?id=' + msg.noticeId, params, "POST").then((res) => {
|
||||
msgList.value[index].isRead = 1
|
||||
updateTabBarBadge()
|
||||
})
|
||||
} catch (err) {
|
||||
console.error('设置消息已读失败:', err)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
badges,
|
||||
msgList,
|
||||
unreadMsgList,
|
||||
unreadCount,
|
||||
fetchMessages,
|
||||
markAsRead,
|
||||
updateTabBarBadge
|
||||
}
|
||||
})
|
||||
164
stores/useRecommedIndexedDBStore.js
Normal file
164
stores/useRecommedIndexedDBStore.js
Normal file
@@ -0,0 +1,164 @@
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia';
|
||||
import {
|
||||
ref
|
||||
} from 'vue'
|
||||
import useDictStore from '@/stores/useDictStore';
|
||||
import jobAnalyzer from '@/utils/jobAnalyzer';
|
||||
import {
|
||||
msg
|
||||
} from '@/common/globalFunction.js'
|
||||
import baseDB from './BaseDBStore';
|
||||
import config from '../config';
|
||||
|
||||
class JobRecommendation {
|
||||
constructor() {
|
||||
this.conditions = {}; // 存储最新的条件及其出现次数
|
||||
this.askHistory = new Map(); // 记录每个条件的最后询问时间
|
||||
this.cooldown = 5 * 60 * 1000; // 冷却时间(单位:毫秒)
|
||||
}
|
||||
|
||||
updateConditions(newConditions) {
|
||||
this.conditions = newConditions;
|
||||
}
|
||||
|
||||
getCurrentTime() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
deleteHostiry(name) {
|
||||
for (const [key, value] of Object.entries(this.conditions)) {
|
||||
if (key === name) {
|
||||
delete this.conditions[key]
|
||||
}
|
||||
}
|
||||
this.askHistory.delete(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个符合条件的推荐问题
|
||||
* @returns {string|null} 返回推荐的问题,或 null(无可询问的)
|
||||
*/
|
||||
getNextQuestion() {
|
||||
const now = this.getCurrentTime();
|
||||
|
||||
// 按照出现次数降序排序
|
||||
const sortedConditions = Object.entries(this.conditions)
|
||||
.sort((a, b) => b[1] - a[1]); // 按出现次数降序排序
|
||||
|
||||
|
||||
for (const [condition, count] of sortedConditions) {
|
||||
const lastAskedTime = this.askHistory.get(condition);
|
||||
|
||||
if (!lastAskedTime || now - lastAskedTime >= this.cooldown) {
|
||||
this.askHistory.set(condition, now);
|
||||
|
||||
return condition;
|
||||
}
|
||||
}
|
||||
|
||||
return null; // 没有可询问的
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算加权用户行为偏好
|
||||
* @param {Object} data - 用户行为数据,包括 categories、experience、areas、salary 等
|
||||
* @param {Object} weights - 每一类行为的权重
|
||||
* @returns {Object} 加权合并后的结果(key 为行为项,value 为权重后的分值)
|
||||
*/
|
||||
function applyWeightsToUserData(data, weights) {
|
||||
const result = {}
|
||||
|
||||
for (const key in data) {
|
||||
if (key === 'salary') {
|
||||
result.salary = weights.salary
|
||||
} else if (typeof data[key] === 'object') {
|
||||
result[key] = {}
|
||||
for (const itemKey in data[key]) {
|
||||
const rawValue = data[key][itemKey]
|
||||
result[key][itemKey] = parseFloat((rawValue * weights[key]).toFixed(2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// **🔹 创建推荐系统**
|
||||
export const jobRecommender = new JobRecommendation();
|
||||
|
||||
export const useRecommedIndexedDBStore = defineStore("indexedDB", () => {
|
||||
const tableName = ref('record')
|
||||
const total = ref(200) // 记录多少条数据
|
||||
|
||||
// 插入数据
|
||||
async function addRecord(payload) {
|
||||
const totalRecords = await baseDB.db.getRecordCount(tableName.value);
|
||||
if (totalRecords >= total.value) {
|
||||
console.log(`⚠数据超过 ${total.value} 条,删除最早的一条...`);
|
||||
await baseDB.db.deleteOldestRecord(tableName.value);
|
||||
}
|
||||
if (!baseDB.isDBReady) await baseDB.initDB();
|
||||
return await baseDB.db.add(tableName.value, payload);
|
||||
}
|
||||
// 清除数据 1、清除数据库数据
|
||||
async function deleteRecords(payload) {
|
||||
if (!baseDB.isDBReady) await baseDB.initDB();
|
||||
try {
|
||||
const jobstr = payload.jobCategory
|
||||
const jobsObj = {
|
||||
'地区': 'jobLocationAreaCodeLabel',
|
||||
'岗位': 'jobCategory',
|
||||
'经验': 'experIenceLabel',
|
||||
}
|
||||
const [name, value] = jobstr.split(':')
|
||||
const nameAttr = jobsObj[name]
|
||||
jobRecommender.deleteHostiry(jobstr)
|
||||
return await baseDB.db.deleteByCondition(tableName.value, (record) => record[nameAttr] ===
|
||||
value);
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// 获取所有数据
|
||||
async function getRecord() {
|
||||
if (!baseDB.isDBReady) await baseDB.initDB();
|
||||
return await baseDB.db.getAll(tableName.value);
|
||||
}
|
||||
|
||||
// 格式化浏览数据岗位数据
|
||||
function JobParameter(job) {
|
||||
const experIenceLabel = useDictStore().dictLabel('experience', job.experience)
|
||||
const jobLocationAreaCodeLabel = useDictStore().dictLabel('area', job.jobLocationAreaCode)
|
||||
return {
|
||||
jobCategory: job.jobCategory,
|
||||
jobTitle: job.jobTitle,
|
||||
minSalary: job.minSalary,
|
||||
maxSalary: job.maxSalary,
|
||||
experience: job.experience,
|
||||
experIenceLabel,
|
||||
jobLocationAreaCode: job.jobLocationAreaCode,
|
||||
jobLocationAreaCodeLabel,
|
||||
createTime: Date.now()
|
||||
}
|
||||
}
|
||||
|
||||
function analyzer(jobsData) {
|
||||
const result = jobAnalyzer.analyze(jobsData) // 转换格式化
|
||||
const result2 = applyWeightsToUserData(result, config.weights) // 添加权重
|
||||
const sort = jobAnalyzer.printUnifiedResults(result2) // 转换格式化
|
||||
return {
|
||||
result,
|
||||
sort
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
addRecord,
|
||||
getRecord,
|
||||
JobParameter,
|
||||
analyzer,
|
||||
deleteRecords
|
||||
};
|
||||
});
|
||||
146
stores/useUserStore.js
Normal file
146
stores/useUserStore.js
Normal file
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia';
|
||||
import {
|
||||
ref
|
||||
} from 'vue'
|
||||
import {
|
||||
createRequest
|
||||
} from '@/utils/request';
|
||||
import similarityJobs from '@/utils/similarity_Job.js';
|
||||
import {
|
||||
UUID
|
||||
} from "@/lib/uuid-min.js";
|
||||
import {
|
||||
useReadMsg
|
||||
} from '@/stores/useReadMsg';
|
||||
|
||||
// 简历完成度计算
|
||||
function getResumeCompletionPercentage(resume) {
|
||||
const requiredFields = [
|
||||
'name',
|
||||
'age',
|
||||
'sex',
|
||||
'birthDate',
|
||||
'education',
|
||||
'politicalAffiliation',
|
||||
'phone',
|
||||
'salaryMin',
|
||||
'salaryMax',
|
||||
'area',
|
||||
'status',
|
||||
'jobTitleId',
|
||||
'jobTitle',
|
||||
];
|
||||
|
||||
const totalFields = requiredFields.length;
|
||||
let filledFields = requiredFields.filter((field) => {
|
||||
const value = resume[field];
|
||||
return value !== null && value !== '' && !(Array.isArray(value) && value.length === 0);
|
||||
}).length;
|
||||
|
||||
return ((filledFields / totalFields) * 100).toFixed(0) + '%';
|
||||
}
|
||||
|
||||
const useUserStore = defineStore("user", () => {
|
||||
// 定义状态
|
||||
const hasLogin = ref(false)
|
||||
// const openId = ref('')
|
||||
const userInfo = ref({});
|
||||
const role = ref({});
|
||||
const token = ref('')
|
||||
const resume = ref({})
|
||||
const Completion = ref('0%')
|
||||
const seesionId = ref(uni.getStorageSync('seesionId') || '')
|
||||
|
||||
const login = (value) => {
|
||||
hasLogin.value = true;
|
||||
userInfo.value = value;
|
||||
openId.value = value.wxOpenId;
|
||||
token.value = value.token;
|
||||
uni.setStorage({
|
||||
key: 'token',
|
||||
data: value.token
|
||||
});
|
||||
}
|
||||
|
||||
const logOut = () => {
|
||||
hasLogin.value = false;
|
||||
token.value = ''
|
||||
resume.value = {}
|
||||
userInfo.value = {}
|
||||
role.value = {}
|
||||
uni.clearStorageSync('userInfo')
|
||||
uni.clearStorageSync('token')
|
||||
uni.redirectTo({
|
||||
url: '/pages/login/login',
|
||||
});
|
||||
}
|
||||
|
||||
const getUserInfo = () => {
|
||||
return new Promise((reslove, reject) => {
|
||||
createRequest('/getInfo', {}, 'get').then((userInfo) => {
|
||||
setUserInfo(userInfo);
|
||||
reslove(userInfo)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const getUserResume = () => {
|
||||
return new Promise((reslove, reject) => {
|
||||
createRequest('/app/user/resume', {}, 'get').then((resume) => {
|
||||
Completion.value = getResumeCompletionPercentage(resume.data)
|
||||
similarityJobs.setUserInfo(resume.data)
|
||||
setUserInfo(resume);
|
||||
reslove(resume)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
const loginSetToken = async (value) => {
|
||||
token.value = value
|
||||
uni.setStorageSync('token', value);
|
||||
// 获取消息列表
|
||||
useReadMsg().fetchMessages()
|
||||
// 获取用户信息
|
||||
return getUserResume()
|
||||
}
|
||||
|
||||
const setUserInfo = (values) => {
|
||||
userInfo.value = values.data;
|
||||
// role.value = values.role;
|
||||
hasLogin.value = true;
|
||||
}
|
||||
|
||||
|
||||
const tokenlogin = (token) => {
|
||||
createRequest('/app/login', {
|
||||
token
|
||||
}).then((resData) => {
|
||||
onsole.log(resData)
|
||||
})
|
||||
}
|
||||
|
||||
const initSeesionId = () => {
|
||||
const seesionIdVal = UUID.generate()
|
||||
uni.setStorageSync('seesionId', seesionIdVal);
|
||||
seesionId.value = seesionIdVal
|
||||
}
|
||||
|
||||
// 导入
|
||||
return {
|
||||
hasLogin,
|
||||
userInfo,
|
||||
token,
|
||||
resume,
|
||||
login,
|
||||
logOut,
|
||||
loginSetToken,
|
||||
getUserResume,
|
||||
initSeesionId,
|
||||
seesionId,
|
||||
Completion
|
||||
}
|
||||
})
|
||||
|
||||
export default useUserStore;
|
||||
340
stores/userChatGroupStore.js
Normal file
340
stores/userChatGroupStore.js
Normal file
@@ -0,0 +1,340 @@
|
||||
import {
|
||||
defineStore
|
||||
} from 'pinia';
|
||||
import {
|
||||
reactive,
|
||||
ref,
|
||||
toRaw
|
||||
} from 'vue'
|
||||
import baseDB from './BaseDBStore';
|
||||
import {
|
||||
msg,
|
||||
CloneDeep,
|
||||
$api,
|
||||
formatDate,
|
||||
insertSortData
|
||||
} from '../common/globalFunction';
|
||||
import {
|
||||
UUID
|
||||
} from '../lib/uuid-min';
|
||||
import config from '../config';
|
||||
import {
|
||||
clearJobMoreMap
|
||||
} from '@/utils/markdownParser';
|
||||
|
||||
const useChatGroupDBStore = defineStore("messageGroup", () => {
|
||||
const tableName = ref('messageGroup')
|
||||
const massageName = ref('messages')
|
||||
|
||||
const messages = ref([]) // 消息列表
|
||||
const isTyping = ref(false) // 是否正在输入
|
||||
const textInput = ref('')
|
||||
// tabel
|
||||
const tabeList = ref([])
|
||||
const chatSessionID = ref('')
|
||||
const lastDateRef = ref('')
|
||||
|
||||
async function init() {
|
||||
// 获取所有数据
|
||||
setTimeout(async () => {
|
||||
if (!baseDB.isDBReady) await baseDB.initDB();
|
||||
const result = await baseDB.db.getAll(tableName.value);
|
||||
// 1、判断是否有数据,没数据请求服务器
|
||||
if (result.length) {
|
||||
console.warn('本地数据库存在数据')
|
||||
const list = result.reverse()
|
||||
const [table, lastData] = insertSortData(list)
|
||||
tabeList.value = table
|
||||
const tabelRow = list[0]
|
||||
chatSessionID.value = tabelRow.sessionId
|
||||
initMessage(tabelRow.sessionId)
|
||||
} else {
|
||||
if (config.OnlyUseCachedDB) return;
|
||||
console.warn('本地数据库不存在数据')
|
||||
getHistory('refresh')
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
async function initMessage(sessionId) {
|
||||
if (!baseDB.isDBReady) await baseDB.initDB();
|
||||
chatSessionID.value = sessionId
|
||||
const list = await baseDB.db.queryByField(massageName.value, 'parentGroupId', sessionId);
|
||||
clearJobMoreMap() // 清空对话 加载更多参数
|
||||
if (list.length) {
|
||||
console.log('本地数据库存在该对话数据', list)
|
||||
messages.value = list
|
||||
} else {
|
||||
console.log('本地数据库不存在该对话数据')
|
||||
getHistoryRecord('refresh')
|
||||
}
|
||||
}
|
||||
|
||||
async function addMessage(payload) {
|
||||
if (!chatSessionID.value) {
|
||||
return msg('请创建对话')
|
||||
}
|
||||
const params = {
|
||||
...payload,
|
||||
parentGroupId: chatSessionID.value,
|
||||
files: payload.files || [],
|
||||
}
|
||||
messages.value.push(params);
|
||||
addMessageIndexdb(params)
|
||||
}
|
||||
|
||||
function toggleTyping(status) {
|
||||
isTyping.value = status
|
||||
}
|
||||
|
||||
async function addTabel(text) {
|
||||
if (!baseDB.isDBReady) await baseDB.initDB();
|
||||
const uuid = UUID.generate() // 对话sessionId
|
||||
let payload = {
|
||||
title: text,
|
||||
createTime: formatDate(Date.now()),
|
||||
sessionId: uuid
|
||||
}
|
||||
const id = await baseDB.db.add(tableName.value, payload);
|
||||
const list = await baseDB.db.getAll(tableName.value);
|
||||
chatSessionID.value = uuid
|
||||
const [result, lastData] = insertSortData(list)
|
||||
tabeList.value = result
|
||||
return id
|
||||
}
|
||||
|
||||
async function addMessageIndexdb(payload) {
|
||||
return await baseDB.db.add(massageName.value, payload);
|
||||
}
|
||||
|
||||
async function getStearm(text, fileUrls = [], progress) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
toggleTyping(true);
|
||||
const customDataID = 'message_' + UUID.generate()
|
||||
const params = {
|
||||
data: text,
|
||||
sessionId: chatSessionID.value,
|
||||
dataId: customDataID
|
||||
};
|
||||
if (fileUrls && fileUrls.length) {
|
||||
params['fileUrl'] = fileUrls.map((item) => item.url);
|
||||
}
|
||||
// ------>
|
||||
const MsgData = {
|
||||
text: text,
|
||||
self: true,
|
||||
displayText: text,
|
||||
files: fileUrls
|
||||
};
|
||||
addMessage(MsgData); // 添加message数据
|
||||
// <------
|
||||
const newMsg = {
|
||||
text: '', // 存储原始结构化内容
|
||||
self: false,
|
||||
displayText: '', // 用于流式渲染展示
|
||||
dataId: customDataID
|
||||
};
|
||||
const index = messages.value.length;
|
||||
messages.value.push(newMsg);
|
||||
|
||||
const rawParts = Array.isArray(text) ? text : [text]; // 统一处理
|
||||
|
||||
// 用于追加每个部分的流式数据
|
||||
let partIndex = 0;
|
||||
|
||||
function handleUnload() {
|
||||
newMsg.parentGroupId = chatSessionID.value;
|
||||
baseDB.db.add(massageName.value, newMsg).then((id) => {
|
||||
messages.value[index] = {
|
||||
...newMsg,
|
||||
id
|
||||
};
|
||||
});
|
||||
}
|
||||
window.addEventListener("unload", handleUnload);
|
||||
|
||||
function onDataReceived(data) {
|
||||
// 支持追加多个部分
|
||||
newMsg.text += data;
|
||||
newMsg.displayText += data;
|
||||
messages.value[index] = {
|
||||
...newMsg
|
||||
};
|
||||
progress && progress();
|
||||
}
|
||||
|
||||
function onError(error) {
|
||||
msg('服务响应异常');
|
||||
reject(error);
|
||||
}
|
||||
|
||||
function onComplete() {
|
||||
messages.value[index] = {
|
||||
...newMsg
|
||||
};
|
||||
toggleTyping(false);
|
||||
window.removeEventListener("unload", handleUnload);
|
||||
handleUnload();
|
||||
resolve();
|
||||
}
|
||||
|
||||
$api.streamRequest('/chat', params, onDataReceived, onError, onComplete);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 状态控制
|
||||
function addNewDialogue() {
|
||||
chatSessionID.value = ''
|
||||
messages.value = []
|
||||
}
|
||||
|
||||
function changeDialogue(item) {
|
||||
chatSessionID.value = item.sessionId
|
||||
initMessage(item.sessionId)
|
||||
}
|
||||
|
||||
// 云端数据
|
||||
function badFeedback(msgs, content = '') {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!msgs.dataId) {
|
||||
return msg('旧数据,没有dataId')
|
||||
}
|
||||
let parmas = {
|
||||
dataId: msgs.dataId,
|
||||
sessionId: msgs.parentGroupId,
|
||||
userBadFeedback: content,
|
||||
}
|
||||
|
||||
const dbData = {
|
||||
...toRaw(msgs),
|
||||
userBadFeedback: content,
|
||||
}
|
||||
|
||||
$api.chatRequest('/stepped', parmas, 'POST').then((res) => {
|
||||
baseDB.db.update(massageName.value, dbData) // 更新本地数据库
|
||||
messages.value.forEach((item) => {
|
||||
if (item.id === dbData.id) {
|
||||
item.userBadFeedback = content
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// 云端数据
|
||||
function getHistory() {
|
||||
$api.chatRequest('/getHistory').then((res) => {
|
||||
if (!res.data.list.length) return
|
||||
let tabel = parseHistory(res.data.list)
|
||||
if (tabel && tabel.length) {
|
||||
const tabelRow = tabel[0] // 默认第一个
|
||||
const [result, lastData] = insertSortData(tabel)
|
||||
// console.log('getHistory insertSortData', result, lastData)
|
||||
chatSessionID.value = tabelRow.sessionId
|
||||
tabeList.value = result
|
||||
getHistoryRecord(false)
|
||||
baseDB.db.add(tableName.value, tabel);
|
||||
lastDateRef.value = lastData
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getHistoryRecord(loading = true) {
|
||||
const params = {
|
||||
sessionId: chatSessionID.value
|
||||
}
|
||||
$api.chatRequest('/detail', params, 'GET', loading).then((res) => {
|
||||
let list = parseHistoryDetail(res.data.list, chatSessionID.value)
|
||||
if (list.length) {
|
||||
baseDB.db.add(massageName.value, list).then((ids) => {
|
||||
messages.value = listAddId(list, ids)
|
||||
});
|
||||
}
|
||||
console.log('解析后:', list)
|
||||
}).catch(() => {
|
||||
msg('请求出现异常,请联系工作人员')
|
||||
})
|
||||
}
|
||||
|
||||
// 解析器
|
||||
function parseHistory(list) {
|
||||
return list.map((item) => ({
|
||||
title: item.title,
|
||||
createTime: item.updateTime,
|
||||
sessionId: item.chatId
|
||||
}))
|
||||
}
|
||||
|
||||
function parseHistoryDetail(list, sessionId) {
|
||||
const arr = []
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const element = list[i];
|
||||
const self = element.obj !== 'AI'
|
||||
let text = ''
|
||||
let files = []
|
||||
for (let j = 0; j < element.value.length; j++) {
|
||||
const obj = element.value[j];
|
||||
if (obj.type === 'text') {
|
||||
text += obj.text.content
|
||||
}
|
||||
if (obj.type === 'file') {
|
||||
files.push(obj.file)
|
||||
}
|
||||
if (obj.type === 'tool') {
|
||||
console.log(obj)
|
||||
}
|
||||
}
|
||||
arr.push({
|
||||
parentGroupId: sessionId,
|
||||
displayText: text,
|
||||
self: self,
|
||||
text: text,
|
||||
userBadFeedback: element.userBadFeedback || '',
|
||||
dataId: element.dataId,
|
||||
files,
|
||||
})
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
|
||||
function listAddId(list, ids) {
|
||||
return list.map((item, index) => ({
|
||||
...item,
|
||||
id: ids[index] || ids
|
||||
}))
|
||||
}
|
||||
|
||||
return {
|
||||
messages,
|
||||
isTyping,
|
||||
textInput,
|
||||
chatSessionID,
|
||||
tabeList,
|
||||
init,
|
||||
toggleTyping,
|
||||
addTabel,
|
||||
addNewDialogue,
|
||||
changeDialogue,
|
||||
getStearm,
|
||||
getHistory,
|
||||
badFeedback
|
||||
};
|
||||
});
|
||||
|
||||
function safeParseJSON(data) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
} catch {
|
||||
return null; // 解析失败,返回 null
|
||||
}
|
||||
}
|
||||
|
||||
export default useChatGroupDBStore
|
||||
Reference in New Issue
Block a user