10 Commits

Author SHA1 Message Date
Apcallover
c5955959c5 flat: ces 2025-12-03 11:01:58 +08:00
Apcallover
16b8ca84cd flat: 性能优化,animation 等\preload等 2025-12-01 20:29:19 +08:00
Apcallover
ecfacd13e3 flat: 优化2 2025-11-30 17:14:41 +08:00
Apcallover
9a38bbd298 flat: 优化 2025-11-30 16:47:06 +08:00
Apcallover
8cf55d3925 flat: 性能优化 2025-11-30 14:26:36 +08:00
Apcallover
0dec1618fa flat: 优化,还是使用原生Tabbar,empty优化 2025-11-30 14:08:16 +08:00
Apcallover
63d0cdb5ad flat: 性能优化,招聘会时间筛选性能优化、精选企业性能优化,封装缓存request、indexDb方法,主要用于不常更新的接口,以达到毫秒级性能 2025-11-29 16:31:34 +08:00
Apcallover
636818361c flat:暂存 2025-11-29 11:48:05 +08:00
Apcallover
23a2b84b4a 合并性能优化缓存备份分支 2025-11-28 19:54:18 +08:00
Apcallover
d84fd90a11 flat: 缓存 2025-11-28 19:47:42 +08:00
51 changed files with 1435 additions and 386 deletions

22
App.vue
View File

@@ -2,10 +2,11 @@
import { reactive, inject, onMounted } from 'vue';
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app';
import useUserStore from './stores/useUserStore';
import usePageAnimation from './hook/usePageAnimation';
import useDictStore from './stores/useDictStore';
const { $api, navTo, appendScriptTagElement, aes_Decrypt, sm2_Decrypt } = inject('globalFunction');
import config from '@/config.js';
usePageAnimation();
const appword = 'aKd20dbGdFvmuwrt'; // 固定值
onLaunch((options) => {
@@ -124,11 +125,22 @@ function loginCallback(userInfo) {
/*每个页面公共css */
@import '@/common/animation.css';
@import '@/common/common.css';
/* 修改pages tabbar样式 H5有效 */
/* 修改pages tabbar样式 H5才有效 */
.uni-tabbar .uni-tabbar__item:nth-child(4) .uni-tabbar__bd .uni-tabbar__icon {
height: 110rpx !important;
width: 122rpx !important;
margin-top: 6rpx;
width: 108rpx !important;
height: 98rpx !important;
margin-top: 0rpx;
transition: transform 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
transform-origin: center center;
/* transition: transform 0.15s ease-in-out; */
/* transform-origin: center center; */
}
.uni-tabbar .uni-tabbar__item:nth-child(4) .uni-tabbar__bd .uni-tabbar__icon:active {
transform: scale(0.8);
transition: transform 0.1s ease-out;
/* animation: jelly 0.5s; */
}
.uni-tabbar-border {

View File

@@ -190,4 +190,38 @@
.btn-rubberBand:active {
-webkit-animation-name: tada;
animation-name: tada
}
@keyframes jelly {
0% {
transform: scale(1);
}
30% {
transform: scale(1.25, 0.75);
}
/* 压扁 */
40% {
transform: scale(0.75, 1.25);
}
/* 拉长 */
50% {
transform: scale(1.15, 0.85);
}
/* 稍微压扁 */
65% {
transform: scale(0.95, 1.05);
}
/* 稍微拉长 */
75% {
transform: scale(1.05, 0.95);
}
100% {
transform: scale(1);
}
}

View File

@@ -468,4 +468,8 @@ html {
.grayscale {
filter: grayscale(100%) opacity(0.6);
}
.height-100 {
height: 100%;
}

View File

@@ -1,6 +1,7 @@
import '@/lib/encryption/sm4.min.js'
import useUserStore from "../stores/useUserStore";
import {
createRequestWithCache,
createRequest,
uploadFile
} from "../utils/request";
@@ -624,6 +625,7 @@ export const $api = {
sendingMiniProgramMessage,
copyText,
aes_Decrypt,
createRequestWithCache
}

View File

@@ -1,5 +1,9 @@
<template>
<view class="empty" :style="{ background: bgcolor, marginTop: mrTop + 'rpx' }">
<view
class="empty"
:class="{ 'position-center': isPosition }"
:style="{ background: bgcolor, marginTop: mrTop + 'rpx' }"
>
<view class="ty_content" :style="{ paddingTop: pdTop + 'rpx' }">
<view class="content_top">
<!-- <view class="content_top btn-shaky"> -->
@@ -30,18 +34,23 @@ export default {
pdTop: {
type: String,
required: false,
default: '80',
default: '0',
},
mrTop: {
type: String,
required: false,
default: '20',
default: '0',
},
pictrue: {
type: String,
required: false,
default: '',
},
isPosition: {
type: Boolean,
required: false,
default: false,
},
},
methods: {},
};
@@ -52,19 +61,33 @@ image {
width: 100%;
height: 100%;
}
.position-center {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.empty {
width: 100%;
min-height: 100vh;
position: relative;
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
// min-height: 100vh;
// height: 400rpx;
// position: relative;
.ty_content {
position: absolute;
left: 50%;
top: 0;
transform: translate(-50%, 0);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
// position: absolute;
// left: 50%;
// top: 0;
// transform: translate(-50%, 0);
.content_top {
width: 450rpx;
height: 322rpx;

View File

@@ -165,7 +165,7 @@ function serchforIt(defaultId) {
return;
}
$api.createRequest('/app/common/jobTitle/treeselect', {}, 'GET').then((resData) => {
const LoadCache = (resData) => {
if (userInfo.value.jobTitleId) {
const ids = userInfo.value.jobTitleId.split(',').map((id) => Number(id));
count.value = ids.length;
@@ -174,7 +174,8 @@ function serchforIt(defaultId) {
state.jobTitleId = userInfo.value.jobTitleId;
state.stations = resData.data;
state.visible = true;
});
};
$api.createRequestWithCache('/app/common/jobTitle/treeselect', {}, 'GET', false, LoadCache).then(LoadCache);
}
const reset = () => {

30
hook/page-animation.css Normal file
View File

@@ -0,0 +1,30 @@
/* #ifdef H5 */
uni-page {
opacity: 1;
will-change: opacity;
}
/* --- 进场 (Enter) --- */
uni-page.animation-enter-from {
opacity: 0;
}
uni-page.animation-enter-active {
transition: opacity 0.2s ease-out;
}
/* --- 离场 (Leave) --- */
uni-page.animation-leave-active {
transition: opacity 0.15s ease-in;
}
uni-page.animation-leave-to {
opacity: 0;
}
/* --- 稳态 --- */
uni-page.animation-show {
opacity: 1;
}
/* #endif */

66
hook/usePageAnimation.js Normal file
View File

@@ -0,0 +1,66 @@
import {
onLaunch
} from '@dcloudio/uni-app'
import {
getCurrentInstance
} from 'vue'
import './page-animation.css'
const DURATION = 130
export default function usePageAnimation() {
// #ifdef H5
const show = () => {
const page = document.querySelector('uni-page')
if (!page) return
const cl = page.classList
cl.add('animation-enter-from')
cl.remove('animation-leave-to', 'animation-leave-active')
requestAnimationFrame(() => {
requestAnimationFrame(() => {
cl.remove('animation-enter-from')
cl.add('animation-enter-active', 'animation-show')
setTimeout(() => {
cl.remove('animation-enter-active')
}, DURATION)
})
})
}
const hide = (next) => {
const page = document.querySelector('uni-page')
if (!page) {
next()
return
}
const cl = page.classList
cl.add('animation-leave-active')
requestAnimationFrame(() => {
cl.remove('animation-show')
cl.add('animation-leave-to')
setTimeout(() => {
cl.remove('animation-leave-active', 'animation-leave-to')
next()
}, DURATION - 50)
})
}
onLaunch(() => {
const instance = getCurrentInstance()
const router = instance?.proxy?.$router
if (router) {
show()
router.beforeEach((to, from, next) => hide(next))
router.afterEach(() => show())
}
})
// #endif
}

View File

@@ -18,10 +18,15 @@
</script>
<title></title>
<!-- eruda -->
<script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
<script>
eruda.init();
</script>
</script> -->
<!-- <script src="https://unpkg.com/vconsole@latest/dist/vconsole.min.js"></script>
<script>
// VConsole 默认会挂载到 `window.VConsole` 上
var vConsole = new window.VConsole();
</script> -->
<!-- 爱山东jssdk 本sdk存在性能问题 -->
<script type="text/javascript" src="https://isdapp.shandong.gov.cn/jmopen/jssdk/index.js"></script>
<!-- 只在内网有效 -->

View File

@@ -1,8 +1,12 @@
import App from '@/App'
import * as Pinia from 'pinia'
import {
createUnistorage
} from "./uni_modules/pinia-plugin-unistorage";
import globalFunction from '@/common/globalFunction'
import '@/lib/string-similarity.min.js'
import similarityJobs from '@/utils/similarity_Job.js';
// 组件
import AppLayout from './components/AppLayout/AppLayout.vue';
import Empty from './components/empty/empty.vue';
@@ -65,7 +69,10 @@ export function createApp() {
app.provide('deviceInfo', globalFunction.getdeviceInfo());
app.use(SelectPopupPlugin);
app.use(Pinia.createPinia());
const store = Pinia.createPinia();
store.use(createUnistorage());
app.use(store);
return {
app,

View File

@@ -1,13 +1,13 @@
<template>
<view class="collection-content">
<renderDeliveryRecord
<renderDeliveryRecord
v-if="pageState.list.length"
seeDate="applyTime"
:list="pageState.list"
:longitude="longitudeVal"
:latitude="latitudeVal">
</renderDeliveryRecord>
<empty v-else pdTop="200"></empty>
:latitude="latitudeVal"
></renderDeliveryRecord>
<empty v-else :is-position="true"></empty>
</view>
</template>
@@ -53,7 +53,7 @@ function getJobList(type = 'add') {
current: pageState.page,
pageSize: pageState.pageSize,
};
$api.createRequest('/app/user/apply/job', params).then((resData) => {
const LoadCache = (resData) => {
const { rows, total } = resData;
if (type === 'add') {
const str = pageState.pageSize * (pageState.page - 1);
@@ -66,8 +66,9 @@ function getJobList(type = 'add') {
// pageState.list = resData.rows;
pageState.total = resData.total;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
console.log(pageState.list);
});
};
$api.createRequestWithCache('/app/user/apply/job', params, 'GET', false, {}, LoadCache).then(LoadCache);
}
</script>
@@ -75,7 +76,8 @@ function getJobList(type = 'add') {
.collection-content{
padding: 1rpx 28rpx 20rpx 28rpx;
background: #F4F4F4;
height: 100%
height: 100%;
min-height: calc(100vh - var(--window-top) - var(--status-bar-height) - var(--window-bottom));
position: relative;
}
</style>

View File

@@ -62,7 +62,7 @@
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="200"></empty>
<empty v-else :is-position="true"></empty>
</view>
</scroll-view>
</view>
@@ -93,10 +93,10 @@ const pageOptions = ref({});
const dataType = ref(1); // 1: 原数据, 2: 第三方数据
onLoad((options) => {
console.log(options);
// console.log(options);
dataType.value = options.dataType ? parseInt(options.dataType) : 1;
pageOptions.value = options;
if (dataType.value === 2) {
// 第三方数据
getCompanyInfo(options.companyId, options.zphId);
@@ -145,13 +145,13 @@ function getCompanyInfo(...args) {
if (dataType.value === 2) {
// 第三方数据接口
const [companyId, zphId] = args;
$api.createRequest(`/app/internal/companyThirdPart/${companyId}/${zphId}`).then((resData) => {
$api.createRequest(`/app/internal/companyThirdPart/${companyId}/${zphId}`, {}, 'GET', true).then((resData) => {
companyInfo.value = resData.data;
});
} else {
// 原数据接口
const [companyId] = args;
$api.createRequest(`/app/company/${companyId}`).then((resData) => {
$api.createRequest(`/app/company/${companyId}`, {}, 'GET', true).then((resData) => {
companyInfo.value = resData.data;
getJobsList();
});
@@ -170,7 +170,7 @@ function getJobsList(type = 'add') {
function getThirdPartyJobsList(type = 'add') {
const { companyId, companyName, zphId } = pageOptions.value;
if (type === 'refresh') {
pageState.current = 1;
pageState.maxPage = 1;
@@ -178,13 +178,18 @@ function getThirdPartyJobsList(type = 'add') {
if (type === 'add' && pageState.current < pageState.maxPage) {
pageState.current += 1;
}
let params = {
current: pageState.current,
pageSize: pageState.pageSize,
};
$api.createRequest(`/app/internal/jobThirdPart?gsID=${companyId}&gsmc=${companyName}&zphID=${zphId}`, params).then((resData) => {
$api.createRequest(
`/app/internal/jobThirdPart?gsID=${companyId}&gsmc=${companyName}&zphID=${zphId}`,
params,
'GET',
true
).then((resData) => {
const { rows, total } = resData;
handleJobsListResponse(type, rows, total, 'current');
});
@@ -198,13 +203,13 @@ function getOriginalJobsList(type = 'add') {
if (type === 'add' && pageState.page < pageState.maxPage) {
pageState.page += 1;
}
let params = {
current: pageState.page,
pageSize: pageState.pageSize,
};
$api.createRequest(`/app/company/job/${companyInfo.value.companyId}`, params).then((resData) => {
$api.createRequest(`/app/company/job/${companyInfo.value.companyId}`, params, 'GET', true).then((resData) => {
const { rows, total } = resData;
handleJobsListResponse(type, rows, total, 'page');
});
@@ -322,6 +327,8 @@ image {
background: #F4F4F4;
.views{
padding: 28rpx
min-height: calc(100% - 56rpx)
position: relative
.Detail-title{
font-weight: 600;
font-size: 32rpx;
@@ -402,4 +409,4 @@ image {
}
}
}
</style>
</style>

View File

@@ -176,12 +176,13 @@ function complete(values) {
}
function getTree() {
$api.createRequest('/app/common/jobTitle/treeselect', {}, 'GET').then((resData) => {
const LoadCache = (resData) => {
if (resData.code === 200) {
dataSource.value = flattenTree(resData.data);
treeDataList.value = resData.data;
}
});
};
$api.createRequestWithCache('/app/common/jobTitle/treeselect', {}, 'GET', false, LoadCache).then(LoadCache);
}
function flattenTree(treeData, parentPath = '') {

View File

@@ -24,15 +24,14 @@
</view>
<scroll-view scroll-y class="main-scroll" @scrolltolower="getJobList('add')">
<view class="one-cards">
<renderJobViewRecord
:list="pageState.list"
v-if="pageState.list.length"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobViewRecord>
<empty v-else pdTop="200"></empty>
<!-- <loadmore ref="loadmoreRef"></loadmore> -->
<renderJobViewRecord
:list="pageState.list"
v-if="pageState.list.length"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobViewRecord>
<empty v-else></empty>
<!-- <loadmore ref="loadmoreRef"></loadmore> -->
</view>
</scroll-view>
</view>
@@ -87,8 +86,6 @@ function toSelectDate() {
});
}
function searchCollection(e) {
const value = e.detail.value;
pageState.search.jobTitle = value;
@@ -167,9 +164,12 @@ image {
.collection-content
height: 100%
display: flex
flex-direction: column
flex-direction: column;
background: #f4f4f4
.collection-search
padding: 10rpx 20rpx;
background: #FFFFFF;
.search-content
position: relative
@@ -216,6 +216,6 @@ image {
.one-cards{
padding: 0 20rpx 20rpx 20rpx;
background: #f4f4f4
height: 100%
}
</style>
</style>

View File

@@ -50,10 +50,11 @@ function delCollectionCard(item) {
}
function getPremiumList() {
$api.createRequest('/app/company/card').then((resData) => {
const LoadCache = (resData) => {
const { rows, total } = resData;
list.value = rows;
});
};
$api.createRequestWithCache('/app/company/card', {}, 'GET', false, {}, LoadCache).then(LoadCache);
}
function seeDetail(item) {

View File

@@ -27,7 +27,7 @@
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderCompanys>
<empty v-else pdTop="200"></empty>
<empty v-else is-position></empty>
</view>
</AppLayout>
</template>
@@ -151,6 +151,8 @@ image {
}
.main-list{
background-color: #F4F4F4;
padding: 1rpx 28rpx 28rpx 28rpx
padding: 1rpx 28rpx 28rpx 28rpx;
min-height: calc(100% - 29rpx);
position: relative
}
</style>

View File

@@ -26,7 +26,7 @@
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobCollectionRecord>
<empty v-else pdTop="200"></empty>
<empty v-else></empty>
</view>
</scroll-view>
</swiper-item>
@@ -44,7 +44,7 @@
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderCompanyCollectionRecord>
<empty v-else pdTop="200"></empty>
<empty v-else></empty>
</view>
</scroll-view>
</swiper-item>
@@ -178,7 +178,8 @@ function getJobList(type = 'add') {
current: pageState.page,
pageSize: pageState.pageSize,
};
$api.createRequest('/app/user/collection/job', params).then((resData) => {
const LoadCache = (resData) => {
console.log(resData);
const { rows, total } = resData;
if (type === 'add') {
const str = pageState.pageSize * (pageState.page - 1);
@@ -191,7 +192,8 @@ function getJobList(type = 'add') {
// pageState.list = resData.rows;
pageState.total = resData.total;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
});
};
$api.createRequestWithCache('/app/user/collection/job', params, 'GET', false, {}, LoadCache).then(LoadCache);
}
function getCompanyList(type = 'add') {
@@ -206,7 +208,7 @@ function getCompanyList(type = 'add') {
current: pageCompanyState.page,
pageSize: pageCompanyState.pageSize,
};
$api.createRequest('/app/user/collection/company', params).then((resData) => {
const LoadCache = (resData) => {
const { rows, total } = resData;
if (type === 'add') {
const str = pageCompanyState.pageSize * (pageCompanyState.page - 1);
@@ -219,7 +221,8 @@ function getCompanyList(type = 'add') {
// pageCompanyState.list = resData.rows;
pageCompanyState.total = resData.total;
pageCompanyState.maxPage = Math.ceil(pageCompanyState.total / pageCompanyState.pageSize);
});
};
$api.createRequestWithCache('/app/user/collection/company', params, 'GET', false, {}, LoadCache).then(LoadCache);
}
</script>
@@ -262,6 +265,7 @@ function getCompanyList(type = 'add') {
.swiper{
height: 100%
.mian{
height: 100%
padding: 0 28rpx 28rpx 28rpx
}
}

View File

@@ -83,7 +83,7 @@
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderCompanysOutData>
<empty v-else pdTop="200"></empty>
<empty v-else is-position></empty>
</view>
</scroll-view>
</view>
@@ -134,7 +134,7 @@ onLoad((options) => {
});
function getJobFairInfo(id, name) {
$api.createRequest(`/app/internal/jobFairThirdPart/${id}`).then((resData) => {
$api.createRequest(`/app/internal/jobFairThirdPart/${id}`, {}, 'GET', true).then((resData) => {
fairInfo.value = resData.data;
hasAppointment();
});
@@ -152,21 +152,24 @@ function getCompanyList(type = 'add') {
current: pageState.current,
pageSize: pageState.pageSize,
};
$api.createRequest(`/app/internal/companyThirdPart/?zphID=${jobFairId}&zphmc=${jobFairName}`, params).then(
(resData) => {
const { rows, total } = resData;
if (type === 'add') {
const str = pageState.pageSize * (pageState.current - 1);
const end = pageState.list.length;
const reslist = rows;
pageState.list.splice(str, end, ...reslist);
} else {
pageState.list = rows;
}
pageState.total = resData.total;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
$api.createRequest(
`/app/internal/companyThirdPart/?zphID=${jobFairId}&zphmc=${jobFairName}`,
params,
'GET',
true
).then((resData) => {
const { rows, total } = resData;
if (type === 'add') {
const str = pageState.pageSize * (pageState.current - 1);
const end = pageState.list.length;
const reslist = rows;
pageState.list.splice(str, end, ...reslist);
} else {
pageState.list = rows;
}
);
pageState.total = resData.total;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
});
}
const hasAppointment = () => {
@@ -433,6 +436,8 @@ image {
background: #F4F4F4;
.views{
padding: 28rpx
min-height: calc(100% - 56rpx);
position: relative
.Detail-title{
font-weight: 600;
font-size: 32rpx;

View File

@@ -24,12 +24,11 @@
<scroll-view class="scroll-view" scroll-y @scrolltolower="scrollBottom">
<view class="list">
<renderJobs
:list="pageState.list"
v-if="pageState.list.length"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="200"></empty>
<empty v-else></empty>
</view>
</scroll-view>
</view>
@@ -140,6 +139,7 @@ function getList(type = 'add', loading = true) {
height: 100%
.list{
padding: 0 28rpx 28rpx 28rpx
height: calc(100% - 28rpx)
}
}
}

View File

@@ -11,7 +11,7 @@
<image src="@/static/icon/collect2.png" v-else @click="jobCollection"></image>
</view>
</template>
<!-- 根据 dataType 显示不同内容 -->
<view class="content" v-show="!isEmptyObject(jobInfo)">
<!-- 顶部信息区域 -->
@@ -23,7 +23,7 @@
:is-month="true"
></Salary-Expectation>
</view>
<view class="top-salary" v-else>
<view class="top-salary" v-else>
<Salary-Expectation
:max-salary="jobInfo.maxSalary"
:min-salary="jobInfo.minSalary"
@@ -35,17 +35,17 @@
<view class="info-img"><image src="/static/icon/post12.png"></image></view>
<!-- 第三方数据展示 -->
<view class="info-text" v-if="dataType === 2">
{{jobInfo.xlyq == '不限' ? '学历不限' : jobInfo.xlyq}}
{{ jobInfo.xlyq == '不限' ? '学历不限' : jobInfo.xlyq }}
</view>
<!-- 原数据展示 -->
<view class="info-text" v-else>
<dict-Label dictType="experience" :value="jobInfo.experience"></dict-Label>
</view>
<view class="info-img mar_le20"><image src="/static/icon/post13.png"></image></view>
<!-- 第三方数据展示 -->
<view class="info-text" v-if="dataType === 2">
{{jobInfo.gwgzjy == '不限' ? '经验不限' : jobInfo.gwgzjy}}
{{ jobInfo.gwgzjy == '不限' ? '经验不限' : jobInfo.gwgzjy }}
</view>
<!-- 原数据展示 -->
<view class="info-text" v-else>
@@ -83,12 +83,7 @@
<view class="content-card">
<view class="card-title">
<text class="title">公司信息</text>
<text
class="btntext button-click"
@click="handleCompanyDetail"
>
单位详情
</text>
<text class="btntext button-click" @click="handleCompanyDetail">单位详情</text>
</view>
<view class="company-info">
<view class="companyinfo-left">
@@ -103,12 +98,12 @@
:value="jobInfo.company?.industry"
></dict-tree-Label>
<span v-if="dataType !== 2 && jobInfo.company?.industry">&nbsp;</span>
<dict-Label
v-if="dataType !== 2"
dictType="scale"
<dict-Label
v-if="dataType !== 2"
dictType="scale"
:value="jobInfo.company?.scale"
></dict-Label>
<span v-if="dataType === 2">{{jobInfo.qyxz}}</span>
<span v-if="dataType === 2">{{ jobInfo.qyxz }}</span>
</view>
<view class="row2">
<text>在招</text>
@@ -156,19 +151,24 @@
</view>
</view>
</view>
<view style="height: 24px"></view>
</view>
<template #footer>
<view class="footer">
<view v-if="dataType==2" class="btn-wq button-click" :class="{'btn-des' : jobInfo.isApply}" @click="jobApply">
<span v-if="jobInfo.isApply"> 已投递 </span>
<span v-if="!jobInfo.isApply"> 立即投递</span>
<view
v-if="dataType == 2"
class="btn-wq button-click"
:class="{ 'btn-des': jobInfo.isApply }"
@click="jobApply"
>
<span v-if="jobInfo.isApply">已投递</span>
<span v-if="!jobInfo.isApply">立即投递</span>
</view>
<view v-else class="btn-wq button-click" @click="jobApply">
<span v-if="jobInfo.isApply"> 立即前往</span>
<span v-if="!jobInfo.isApply">立即投递 </span>
<span v-if="jobInfo.isApply">立即前往</span>
<span v-if="!jobInfo.isApply">立即投递</span>
</view>
</view>
</template>
@@ -232,12 +232,12 @@ function getDetail(jobId) {
if (dataType.value === 2) {
// 第三方数据接口
return new Promise((reslove, reject) => {
$api.createRequest(`/app/internal/jobThirdPart/${jobId}`).then((resData) => {
$api.createRequest(`/app/internal/jobThirdPart/${jobId}`, {}, 'GET', true).then((resData) => {
const { gsID, gsmc, zphID } = resData.data;
jobInfo.value = resData.data;
reslove(resData.data);
getCompanyIsAJobs(gsID, gsmc, zphID);
if (resData.data.latitude && resData.data.longitude) {
initMapCovers(resData.data.latitude, resData.data.longitude, resData.data.gsmc);
}
@@ -245,12 +245,12 @@ function getDetail(jobId) {
});
} else {
// 原数据接口
$api.createRequest(`/app/job/${jobId}`).then((resData) => {
$api.createRequest(`/app/job/${jobId}`, {}, 'GET', true).then((resData) => {
const { latitude, longitude, companyName, companyId } = resData.data;
jobInfo.value = resData.data;
getCompanyIsAJobs(companyId);
getCompetivetuveness(jobId);
if (latitude && longitude) {
initMapCovers(latitude, longitude, companyName);
}
@@ -315,12 +315,12 @@ function jobApply() {
if (dataType.value === 2) {
// 第三方数据申请逻辑
const params = {
jobid:jobInfo.value.id,
jobname:jobInfo.value.gwmc
}
jobid: jobInfo.value.id,
jobname: jobInfo.value.gwmc,
};
if (jobInfo.value.isApply) {
$api.msg('已经投递过该岗位了~');
return ;
return;
} else {
$api.createRequest(`/app/internal/sendResume`, params, 'POST').then((resData) => {
$api.msg('投递成功');
@@ -380,7 +380,9 @@ function jobCollection() {
// 处理公司详情跳转
function handleCompanyDetail() {
if (dataType.value === 2) {
navTo(`/packageA/pages/UnitDetails/UnitDetails?companyId=${jobInfo.value.gsID}&companyName=${jobInfo.value.gsmc}&zphId=${jobInfo.value.zphID}&dataType=2`);
navTo(
`/packageA/pages/UnitDetails/UnitDetails?companyId=${jobInfo.value.gsID}&companyName=${jobInfo.value.gsmc}&zphId=${jobInfo.value.zphID}&dataType=2`
);
} else {
navTo(`/packageA/pages/UnitDetails/UnitDetails?companyId=${jobInfo.value.company.companyId}`);
}
@@ -660,4 +662,4 @@ for i in 0..100
box-shadow: 0rpx -4rpx 24rpx 0rpx rgba(11,44,112,0.12);
}
}
</style>
</style>

View File

@@ -12,10 +12,19 @@
</view>
</view>
<view class="main">
<scroll-view scroll-y>
<view v-if="pageState.list.length">
<scroll-view class="height-100" scroll-y>
<view>
<view class="card" v-for="(item, index) in pageState.list" :key="index">
<view @click="navTo('/packageA/pages/exhibitors/exhibitors?jobFairId=' + item.zphID + '&jobFairName=' + item.zphmc)">
<view
@click="
navTo(
'/packageA/pages/exhibitors/exhibitors?jobFairId=' +
item.zphID +
'&jobFairName=' +
item.zphmc
)
"
>
<view class="card-row">
<Countdown :startTime="item.zphjbsj" :endTime="item.zphjzsj" />
</view>
@@ -37,7 +46,7 @@
</view>
</view>
</view>
<empty v-else pdTop="200"></empty>
<empty v-if="!pageState.list.length"></empty>
</scroll-view>
</view>
</view>
@@ -72,7 +81,7 @@ const ranOptions = ref([
]);
function isTimePassed(timeStr) {
if(!timeStr) return false
if (!timeStr) return false;
const targetTime = new Date(timeStr.replace(/-/g, '/')).getTime(); // 兼容格式
const now = Date.now();
return now < targetTime;
@@ -95,16 +104,14 @@ function updateCancel(item) {
content: '确定要取消预约吗?',
showCancel: true,
success: ({ confirm, cancel }) => {
if(confirm){
$api.createRequest(`/app/fair/collection/${fairId}`, {}, 'DELETE').then((resData) => {
if (confirm) {
$api.createRequest(`/app/fair/collection/${fairId}`, {}, 'DELETE').then((resData) => {
getList('refresh');
$api.msg('取消预约成功');
});
}
}
})
},
});
}
function getList(type = 'add', loading = true) {
@@ -120,7 +127,7 @@ function getList(type = 'add', loading = true) {
pageSize: pageState.pageSize,
type: ranItem.value.value,
};
$api.createRequest('/app/user/collection/fair', params).then((resData) => {
const LoadCache = (resData) => {
const { rows, total } = resData;
if (type === 'add') {
const str = pageState.pageSize * (pageState.page - 1);
@@ -133,7 +140,8 @@ function getList(type = 'add', loading = true) {
// pageState.list = resData.rows;
pageState.total = resData.total;
pageState.maxPage = Math.ceil(pageState.total / pageState.pageSize);
});
};
$api.createRequestWithCache('/app/user/collection/fair', params, 'GET', false, {}, LoadCache).then(LoadCache);
}
</script>
@@ -178,7 +186,7 @@ function getList(type = 'add', loading = true) {
display: flex
align-items: center
}
}
.card-Title{
font-family: 'PingFangSC-Medium', 'PingFang SC', 'Helvetica Neue', Helvetica, Arial, 'Microsoft YaHei', sans-serif;

View File

@@ -26,7 +26,7 @@
<view
class="item button-click"
:class="{
optional:hasZphInData(item),
optional: item.isThisMonth && hasZphInData(item),
noOptional: !item.isThisMonth,
active: current.date === item.date && item.isThisMonth,
}"
@@ -60,10 +60,9 @@ const pages = reactive({
month: 0,
});
const hasZphDateArray = ref([])
const hasZphDateArray = ref([]);
onLoad((options) => {
updateDateArray()
if (options.date) {
current.value = {
date: options?.date || null,
@@ -81,20 +80,30 @@ onLoad((options) => {
addMonth();
});
}
if (options.entrance === 'careerfair') {
updateDateArray();
}
});
function hasZphInData(item) {
return hasZphDateArray.value.some(date=>date == item.date)
if (!item || typeof item.date !== 'string') {
return false;
}
const dateArray = Array.isArray(hasZphDateArray.value) ? hasZphDateArray.value : [];
return dateArray.some((date) => {
return typeof date === 'string' && date === item.date;
});
}
async function updateDateArray() {
if(localStorage.getItem('hasZphDateArray')) hasZphDateArray.value = localStorage.getItem('hasZphDateArray')
let res = await $api.createRequest('/app/internal/getDateList', {}, 'get')
if(res.data){
hasZphDateArray.value = res.data
localStorage.setItem('hasZphDateArray',res.data)
}
const LoadCache = (resData) => {
if (resData.code === 200) {
hasZphDateArray.value = resData.data;
}
};
$api.createRequestWithCache('/app/internal/getDateList', {}, 'GET', false, {}, LoadCache).then(LoadCache);
}
function backParams() {

View File

@@ -227,8 +227,8 @@
]
}],
"tabBar": {
"custom": true,
"display": "none",
// "custom": true,
// "display": "none",
"color": "#5E5F60",
"selectedColor": "#256BFA",
"borderStyle": "black",

View File

@@ -37,7 +37,7 @@
<!-- 主体内容区域 -->
<view class="container-main">
<scroll-view scroll-y class="main-scroll" @scrolltolower="handleScrollToLower">
<view class="cards" v-if="fairList.length">
<view class="cards">
<view
class="card press-button"
v-for="(item, index) in fairList"
@@ -80,7 +80,7 @@
<view class="card-footer">内容简介{{ item.zphjj }}</view>
</view>
</view>
<empty v-else pdTop="200"></empty>
<empty v-if="!fairList.length" pdTop="200"></empty>
</scroll-view>
</view>
<Tabbar :currentpage="1"></Tabbar>
@@ -122,7 +122,7 @@ onLoad(() => {
startDate: currentDate,
});
weekList.value = result;
currentDay.value.fullDate = result[0].fullDate
currentDay.value.fullDate = result[0].fullDate;
getFair('refresh');
});
@@ -162,12 +162,11 @@ function seemsg(index) {
}
const handleScrollToLower = () => {
return
return;
getFair();
console.log('触底');
};
function getFair(type = 'add') {
if (type === 'refresh') {
pageState.page = 1;
@@ -194,7 +193,7 @@ function getFair(type = 'add') {
// const end = fairList.value.length;
// const reslist = rows;
// fairList.value.splice(str, end, ...reslist);
fairList.value = rows
fairList.value = rows;
} else {
fairList.value = rows;
}

View File

@@ -13,7 +13,14 @@
</view>
<view class="header-input btn-feel">
<uni-icons class="iconsearch" color="#666666" type="search" size="18"></uni-icons>
<input v-model="pageState.zphmc" confirm-type="search" @confirm="getFair" class="input" placeholder="招聘会" placeholder-class="inputplace" />
<input
v-model="pageState.zphmc"
confirm-type="search"
@confirm="getFair"
class="input"
placeholder="招聘会"
placeholder-class="inputplace"
/>
</view>
<view class="header-date">
<view class="data-week">
@@ -37,12 +44,19 @@
<!-- 主体内容区域 -->
<view class="container-main">
<scroll-view scroll-y class="main-scroll" @scrolltolower="handleScrollToLower">
<view class="cards" v-if="fairList.length">
<view class="cards">
<view
class="card press-button"
v-for="(item, index) in fairList"
:key="index"
@click="navTo('/packageA/pages/exhibitors/exhibitors?jobFairId=' + item.zphID + '&jobFairName=' + item.zphmc)"
@click="
navTo(
'/packageA/pages/exhibitors/exhibitors?jobFairId=' +
item.zphID +
'&jobFairName=' +
item.zphmc
)
"
>
<view class="card-title">{{ item.zphmc }}</view>
<view class="card-row">
@@ -80,10 +94,10 @@
<view class="card-footer">内容简介{{ item.zphjj }}</view>
</view>
</view>
<empty v-else pdTop="200"></empty>
<empty v-if="!fairList.length"></empty>
</scroll-view>
</view>
<Tabbar :currentpage="1"></Tabbar>
<!-- <Tabbar :currentpage="1"></Tabbar> -->
</view>
</view>
</template>
@@ -95,7 +109,7 @@ import Tabbar from '@/components/tabbar/midell-box.vue';
import useLocationStore from '@/stores/useLocationStore';
import { storeToRefs } from 'pinia';
const { longitudeVal, latitudeVal } = storeToRefs(useLocationStore());
const { $api, navTo, cloneDeep,debounce } = inject('globalFunction');
const { $api, navTo, cloneDeep, debounce } = inject('globalFunction');
const weekList = ref([]);
const fairList = ref([]);
const currentDay = ref({});
@@ -108,7 +122,7 @@ const pageState = reactive({
total: 0,
maxPage: 2,
pageSize: 10,
zphmc:''
zphmc: '',
});
onLoad(() => {
@@ -122,7 +136,7 @@ onLoad(() => {
startDate: currentDate,
});
weekList.value = result;
currentDay.value.fullDate = result[0].fullDate
currentDay.value.fullDate = result[0].fullDate;
getFair('refresh');
});
@@ -130,6 +144,7 @@ function toSelectDate() {
navTo('/packageA/pages/selectDate/selectDate', {
query: {
date: currentDay.value.fullDate,
entrance: 'careerfair',
},
onBack: (res) => {
console.log(res);
@@ -162,12 +177,11 @@ function seemsg(index) {
}
const handleScrollToLower = () => {
return
return;
getFair();
console.log('触底');
};
function getFair(type = 'add') {
if (type === 'refresh') {
pageState.page = 1;
@@ -177,7 +191,7 @@ function getFair(type = 'add') {
pageState.page += 1;
}
let params = {
zphmc:pageState.zphmc,
zphmc: pageState.zphmc,
// current: pageState.page,
// pageSize: pageState.pageSize,
};
@@ -187,14 +201,14 @@ function getFair(type = 'add') {
if (currentDay.value?.fullDate) {
params.zphjbsj = currentDay.value.fullDate.replace(/-/g, '');
}
$api.createRequest('/app/internal/jobFairThirdPart', params).then((resData) => {
$api.createRequest('/app/internal/jobFairThirdPart', params, 'GET', true).then((resData) => {
const { rows, total } = resData;
if (type === 'add') {
// const str = pageState.pageSize * (pageState.page - 1);
// const end = fairList.value.length;
// const reslist = rows;
// fairList.value.splice(str, end, ...reslist);
fairList.value = rows
fairList.value = rows;
} else {
fairList.value = rows;
}

View File

@@ -63,9 +63,9 @@
<ai-paging ref="paging"></ai-paging>
</view>
<!-- 自定义tabbar -->
<view class="chatmain-footer" v-show="!isDrawerOpen">
<!-- <view class="chatmain-footer" v-show="!isDrawerOpen">
<Tabbar :currentpage="2"></Tabbar>
</view>
</view> -->
</view>
</view>
</template>
@@ -213,27 +213,48 @@ footer-height = 98rpx
background: #FFFFFF;
display: flex
flex-direction: column
.drawer-user
border-top: 1rpx solid rgba(0,0,0,.1);
padding: 20rpx 28rpx
display: flex
.drawer-user {
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
padding: 24rpx 32rpx;
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
background-color: #ffffff;
color: #333333;
font-weight: 500;
align-items: center
position: relative
margin-bottom: calc( 32rpx + var(--window-bottom)); /*兼容 IOS<11.2*/
margin-bottom: calc( 32rpx +var(--window-bottom)); /*兼容 IOS>11.2*/
color: #000000
.drawer-user-img
width: 57.2rpx;
height: 57.2rpx
margin-right: 20rpx
.drawer-user-setting
width: 48rpx
height: 48rpx
position: absolute
top: 50%
right: 28rpx
transform: translate(0,-50%)
font-size: 28rpx;
&:active {
background-color: #f9f9f9;
}
.drawer-user-img {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
margin-right: 24rpx;
background-color: #eee;
flex-shrink: 0;
}
.user-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 30rpx;
}
.drawer-user-setting {
width: 48rpx;
height: 48rpx;
margin-left: auto;
opacity: 0.8;
}
}
.drawer-title
height: header-height;
line-height: header-height;

View File

@@ -268,10 +268,10 @@ import WaveDisplay from './WaveDisplay.vue';
import FileIcon from './fileIcon.vue';
import FileText from './fileText.vue';
// 系统功能hook和阿里云hook
// import { useAudioRecorder } from '@/hook/useRealtimeRecorder.js';
import { useAudioRecorder } from '@/hook/useSystemSpeechReader.js';
// import { useTTSPlayer } from '@/hook/useTTSPlayer.js';
import { useTTSPlayer } from '@/hook/useSystemPlayer.js';
import { useAudioRecorder } from '@/hook/useRealtimeRecorder.js';
// import { useAudioRecorder } from '@/hook/useSystemSpeechReader.js';
import { useTTSPlayer } from '@/hook/useTTSPlayer.js';
// import { useTTSPlayer } from '@/hook/useSystemPlayer.js';
// 全局
const { $api, navTo, throttle } = inject('globalFunction');
const emit = defineEmits(['onConfirm']);
@@ -632,7 +632,7 @@ function readMarkdown(value, index) {
if (isPaused.value) {
resume();
} else {
console.log(value, speechIndex.value, index, isPaused.value)
// console.log(value, speechIndex.value, index, isPaused.value)
speak(value);
}
}

View File

@@ -66,7 +66,7 @@
</view>
<view class="table-list">
<scroll-view :scroll-y="true" class="falls-scroll" @scroll="handleScroll" @scrolltolower="scrollBottom">
<view class="falls" v-if="list.length">
<view class="falls">
<custom-waterfalls-flow
:column="columnCount"
:columnSpace="columnSpace"
@@ -142,7 +142,7 @@
</custom-waterfalls-flow>
<loadmore ref="loadmoreRef"></loadmore>
</view>
<empty v-else pdTop="200"></empty>
<empty v-if="!list.length"></empty>
</scroll-view>
</view>
<!-- 筛选 -->
@@ -298,6 +298,7 @@ function nextDetail(job) {
function openFilter() {
showFilter.value = true;
emits('onShowTabbar', false);
uni.hideTabBar();
selectFilterModel.value?.open({
title: '筛选',
maskClick: true,
@@ -310,10 +311,12 @@ function openFilter() {
}
showFilter.value = false;
getJobList('refresh');
uni.showTabBar();
},
cancel: () => {
showFilter.value = false;
emits('onShowTabbar', true);
uni.showTabBar();
},
});
}

View File

@@ -41,7 +41,7 @@
</swiper>
</view>
<Tabbar v-show="showTabbar" :currentpage="0"></Tabbar>
<!-- <Tabbar v-show="showTabbar" :currentpage="0"></Tabbar> -->
<!-- maskFristEntry -->
<view class="maskFristEntry" v-if="maskFristEntry">
@@ -49,7 +49,7 @@
<text class="text1">左滑查看视频</text>
<text class="text2">快去体验吧</text>
<view class="goExperience" @click="goExperience">去体验</view>
<view class="maskFristEntry-Close" @click="closeFristEntry">1</view>
<view class="maskFristEntry-Close" @click="closeFristEntry"></view>
</view>
</view>
</view>
@@ -80,7 +80,16 @@ onLoad(() => {
// 判断浏览器是否有 fristEntry 第一次进入
let fristEntry = uni.getStorageSync('fristEntry') === false ? false : true; // 默认未读
maskFristEntry.value = fristEntry;
// maskFristEntry.value = true;
if (fristEntry) {
uni.hideTabBar();
}
// 预加载较重页面
setTimeout(() => {
uni.preloadPage({ url: '/packageA/pages/post/post' });
uni.preloadPage({ url: '/pages/nearby/nearby' });
uni.preloadPage({ url: '/pages/chat/chat' });
uni.preloadPage({ url: '/packageA/pages/choiceness/choiceness' });
}, 3000);
});
onShow(() => {
@@ -187,10 +196,12 @@ function changeSwiperMsgType(e) {
function closeFristEntry() {
uni.setStorageSync('fristEntry', false);
maskFristEntry.value = false;
uni.showTabBar();
}
function goExperience() {
closeFristEntry();
uni.showTabBar();
state.current = 1;
}
</script>

View File

@@ -241,9 +241,10 @@ function nextStep() {
// 获取职位
function getTreeselect() {
$api.createRequest('/app/common/jobTitle/treeselect', {}, 'GET').then((resData) => {
const LoadCache = (resData) => {
state.station = resData.data;
});
};
$api.createRequestWithCache('/app/common/jobTitle/treeselect', {}, 'GET', false, LoadCache).then(LoadCache);
}
function loginbackdoor() {

View File

@@ -95,9 +95,9 @@
></uni-popup-dialog>
</uni-popup>
</view>
<template #footer>
<!-- <template #footer>
<Tabbar :currentpage="4"></Tabbar>
</template>
</template> -->
</AppLayout>
</template>
@@ -110,13 +110,13 @@ import FileUploader from '@/utils/FileUploader.js';
const { $api, navTo } = inject('globalFunction');
import useUserStore from '@/stores/useUserStore';
const popup = ref(null);
const { userInfo, Completion } = storeToRefs(useUserStore());
const counts = ref({});
const { userInfo, Completion, counts } = storeToRefs(useUserStore());
function logOut() {
popup.value.open();
}
onShow(() => {
getUserstatistics();
useUserStore().getUserstatistics();
});
function close() {
@@ -129,12 +129,6 @@ function confirm() {
const isAbove90 = (percent) => parseFloat(percent) < 90;
function getUserstatistics() {
$api.createRequest('/app/user/statistics').then((resData) => {
counts.value = resData.data;
});
}
function selectFile() {
// FileUploader.showMenuAndUpload({
// success: function (res) {

View File

@@ -40,7 +40,7 @@
</swiper>
</view>
<Tabbar :currentpage="3"></Tabbar>
<!-- <Tabbar :currentpage="3"></Tabbar> -->
</view>
</view>
</template>

View File

@@ -35,6 +35,7 @@
<view class="info-text line_2">{{ item.subTitle || '消息' }}</view>
</view>
</view>
<empty v-if="!msgList.length"></empty>
</view>
</scroll-view>
</template>
@@ -83,6 +84,7 @@ defineExpose({ loadData });
}
.scrollmain{
padding: 28rpx
height: calc(100% - 56rpx)
}
.read{

View File

@@ -33,6 +33,7 @@
<view class="info-text line_2">{{ item.subTitle || '消息' }}</view>
</view>
</view>
<empty v-if="!unreadMsgList.length"></empty>
</view>
</scroll-view>
</template>
@@ -69,6 +70,7 @@ defineExpose({ loadData });
}
.scrollmain{
padding: 28rpx
height: calc(100% - 56rpx)
}
.read{

View File

@@ -69,14 +69,9 @@
</view>
</view>
<view class="one-cards">
<renderJobs
v-if="list.length"
:list="list"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="60"></empty>
<loadmore ref="loadmoreRef"></loadmore>
<renderJobs :list="list" :longitude="longitudeVal" :latitude="latitudeVal"></renderJobs>
<empty v-if="!list.length"></empty>
<loadmore v-show="list.length > pageState.pageSize" ref="loadmoreRef"></loadmore>
</view>
</view>
<!-- 筛选 -->
@@ -340,15 +335,18 @@ defineExpose({ loadData, handleFilterConfirm });
color: #4778EC !important
.nearby-scroll
overflow: hidden;
height: 100%;
background: #f4f4f4;
.two-head
margin: 22rpx;
padding: 22rpx;
display: flex;
flex-direction: column
flex-wrap: no-wrap
// grid-template-columns: repeat(4, 1fr);
// grid-column-gap: 10rpx;
// grid-row-gap: 24rpx;
border-radius: 17rpx 17rpx 17rpx 17rpx;
background: #FFFFFF
// border-radius: 17rpx 17rpx 17rpx 17rpx;
.head-all{
display: flex;
justify-content: space-between;
@@ -380,15 +378,21 @@ defineExpose({ loadData, handleFilterConfirm });
border-radius: 12rpx 12rpx 12rpx 12rpx;
.nearby-list
border-top: 2rpx solid #EBEBEB;
height: 100%
min-height: calc(100% - 140rpx)
background: #f4f4f4
display: flex;
flex-direction: column;
.one-cards{
height: 100%
display: flex;
flex-direction: column;
padding: 0 20rpx 20rpx 20rpx;
background: #f4f4f4
flex: 1
}
.nav-filter
padding: 16rpx 28rpx 0 28rpx
background: #ffffff
.filter-top
display: flex
justify-content: space-between;
@@ -447,4 +451,4 @@ defineExpose({ loadData, handleFilterConfirm });
height: 26rpx;
.active
transform: rotate(180deg)
</style>
</style>

View File

@@ -74,14 +74,9 @@
</view>
</view>
<view class="one-cards">
<renderJobs
v-if="list.length"
:list="list"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="60"></empty>
<loadmore ref="loadmoreRef"></loadmore>
<renderJobs :list="list" :longitude="longitudeVal" :latitude="latitudeVal"></renderJobs>
<empty v-if="!list.length"></empty>
<loadmore v-show="list.length > pageState.pageSize" ref="loadmoreRef"></loadmore>
</view>
</view>
<!-- 筛选 -->
@@ -364,20 +359,28 @@ defineExpose({ loadData, handleFilterConfirm });
}
.nearby-scroll
overflow: hidden;
height: 100%;
background: #f4f4f4;
.nearby-map
height: 767rpx;
background: #e8e8e8;
overflow: hidden
.nearby-list
min-height: calc(100% - 384rpx)
background: #f4f4f4
display: flex;
flex-direction: column;
.one-cards{
display: flex;
flex-direction: column;
padding: 0 20rpx 20rpx 20rpx;
background: #f4f4f4
height: 100%
flex: 1
}
.nav-filter
padding: 16rpx 28rpx 0 28rpx
background: #ffffff
.filter-top
display: flex
justify-content: space-between;

View File

@@ -95,14 +95,9 @@
</view>
</view>
<view class="one-cards">
<renderJobs
v-if="list.length"
:list="list"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="60"></empty>
<loadmore ref="loadmoreRef"></loadmore>
<renderJobs :list="list" :longitude="longitudeVal" :latitude="latitudeVal"></renderJobs>
<empty v-if="!list.length"></empty>
<loadmore v-show="list.length > pageState.pageSize" ref="loadmoreRef"></loadmore>
</view>
</view>
<!-- 筛选 -->
@@ -359,9 +354,12 @@ defineExpose({ loadData, handleFilterConfirm });
color: #4778EC !important;
.nearby-scroll
overflow: hidden;
background: #f4f4f4;
height: 100%
.three-head
margin: 24rpx 0 0 0;
// margin: 24rpx 0 0 0;
padding: 26rpx 0 0 0;
background: #FFFFFF;
border-radius: 17rpx 17rpx 17rpx 17rpx;
.one-picker
height: 100%
@@ -482,14 +480,21 @@ defineExpose({ loadData, handleFilterConfirm });
z-index: 1;
.nearby-list
border-top: 2rpx solid #EBEBEB;
min-height: calc(100% - 222rpx)
background: #f4f4f4
display: flex;
flex-direction: column;
.one-cards{
height: 100%
display: flex;
flex-direction: column;
padding: 0 20rpx 20rpx 20rpx;
background: #f4f4f4
flex: 1
}
.nav-filter
padding: 16rpx 28rpx 0 28rpx
background: #ffffff
.filter-top
display: flex
justify-content: space-between;

View File

@@ -65,14 +65,9 @@
</view>
</view>
<view class="one-cards">
<renderJobs
v-if="list.length"
:list="list"
:longitude="longitudeVal"
:latitude="latitudeVal"
></renderJobs>
<empty v-else pdTop="60"></empty>
<loadmore ref="loadmoreRef"></loadmore>
<renderJobs :list="list" :longitude="longitudeVal" :latitude="latitudeVal"></renderJobs>
<empty v-if="!list.length"></empty>
<loadmore v-show="list.length > pageState.pageSize" ref="loadmoreRef"></loadmore>
</view>
</view>
<!-- 筛选 -->
@@ -255,10 +250,13 @@ defineExpose({ loadData, handleFilterConfirm });
color: #4778EC !important
.nearby-scroll
overflow: hidden;
height: 100%;
background: #f4f4f4;
.two-head
margin: 22rpx;
padding: 22rpx;
display: flex;
flex-wrap: wrap
background: #FFFFFF;
// grid-template-columns: repeat(4, 1fr);
// grid-column-gap: 10rpx;
// grid-row-gap: 24rpx;
@@ -284,14 +282,21 @@ defineExpose({ loadData, handleFilterConfirm });
border-radius: 12rpx 12rpx 12rpx 12rpx;
.nearby-list
border-top: 2rpx solid #EBEBEB;
min-height: calc(100% - 252rpx)
background: #f4f4f4
display: flex;
flex-direction: column;
.one-cards{
display: flex;
flex-direction: column;
padding: 0 20rpx 20rpx 20rpx;
background: #f4f4f4
height: 100%
flex: 1
}
.nav-filter
padding: 16rpx 28rpx 0 28rpx
background: #ffffff
.filter-top
display: flex
justify-content: space-between;

View File

@@ -1,73 +0,0 @@
// 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

View File

@@ -12,27 +12,25 @@ import {
$api,
} from '../common/globalFunction';
// 控制消息
// 常量定义:消息在 TabBar 的索引位置
const TABBAR_INDEX = 3;
export const useReadMsg = defineStore('readMsg', () => {
const msgList = ref([])
// 用于自定义 Tabbar 组件的渲染
const badges = ref([{
count: 0
},
{
count: 0
},
{
count: 0
},
{
count: 0
},
{
count: 0
},
])
count: 0
}, {
count: 0
}, {
count: 0
}, {
count: 0
}, {
count: 0
}])
// 计算总未读数量,基于 notReadCount 字段
// 计算总未读数量
const unreadCount = computed(() =>
msgList.value.reduce((sum, msg) => sum + (msg.notReadCount || 0), 0)
)
@@ -42,40 +40,49 @@ export const useReadMsg = defineStore('readMsg', () => {
msgList.value.filter(msg => msg.notReadCount > 0)
)
// 设置 TabBar 角标
function updateTabBarBadge() {
function updateBadgeEffect() {
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
// 处理显示文本超过99显示99+
const countStr = count > 99 ? '99+' : String(count)
// 1. 更新内部状态 (用于自定义 UI)
if (badges.value[TABBAR_INDEX]) {
badges.value[TABBAR_INDEX].count = count === 0 ? 0 : countStr
}
// 2. 更新系统原生 TabBar
// 加 try-catch 防止在非 Tabbar 页面或加栽未完成时报错
try {
if (count > 0) {
uni.setTabBarBadge({
index: TABBAR_INDEX,
text: countStr
})
} else {
uni.removeTabBarBadge({
index: TABBAR_INDEX
})
}
} else {
badges.value[index] = {
count: countVal
}
uni.setTabBarBadge({
index,
text: countVal
})
} catch (e) {
console.warn('TabBar Badge 更新失败(可能当前非TabBar页面):', e)
}
}
watch(unreadCount, () => {
updateBadgeEffect()
console.log('value', unreadCount.value)
}, {
immediate: true
})
// 拉取消息列表
async function fetchMessages() {
try {
$api.createRequest('/app/notice/info', {
const res = await $api.createRequest('/app/notice/info', {
isRead: 1
}, "GET").then((res) => {
msgList.value = res.data || []
updateTabBarBadge()
})
}, "GET")
msgList.value = res.data || []
} catch (err) {
console.error('获取消息失败:', err)
}
@@ -83,17 +90,23 @@ export const useReadMsg = defineStore('readMsg', () => {
// 设置为已读
async function markAsRead(item, index) {
const msg = msgList.value[index]
if (!msg || msg.isRead === 1) return
const targetMsg = msgList.value[index]
if (!targetMsg) return
// 如果已经是已读,直接返回,避免无效请求
// 假设服务端逻辑是isRead=1 表示已读 (注意检查你的字段定义)
// 你的原代码判断是 if (msg.isRead === 1) return如果是这样下面请求成功应该设为 1
// 但通常未读是0已读是1。这里维持你原有的逻辑假设服务端把 notReadCount 清零
try {
let params = {
id: msg.noticeId
id: targetMsg.noticeId
}
$api.createRequest('/app/notice/read?id=' + msg.noticeId, params, "POST").then((res) => {
msgList.value[index].isRead = 1
updateTabBarBadge()
})
await $api.createRequest('/app/notice/read?id=' + targetMsg.noticeId, params, "POST")
// 更新本地数据
msgList.value[index].notReadCount = 0
msgList.value[index].isRead = 1 // 标记已读状态
} catch (err) {
console.error('设置消息已读失败:', err)
}
@@ -106,6 +119,8 @@ export const useReadMsg = defineStore('readMsg', () => {
unreadCount,
fetchMessages,
markAsRead,
updateTabBarBadge
updateTabBarBadge: updateBadgeEffect
}
}, {
unistorage: true, // 开启持久化
})

View File

@@ -9,7 +9,7 @@ import jobAnalyzer from '@/utils/jobAnalyzer';
import {
msg
} from '@/common/globalFunction.js'
import baseDB from './BaseDBStore';
import baseDB from '@/utils/db.js';
import config from '../config';
class JobRecommendation {

View File

@@ -14,6 +14,10 @@ import {
import {
useReadMsg
} from '@/stores/useReadMsg';
import {
msg,
$api,
} from '../common/globalFunction';
// 简历完成度计算
function getResumeCompletionPercentage(resume) {
@@ -52,7 +56,8 @@ const useUserStore = defineStore("user", () => {
const token = ref('')
const resume = ref({})
const Completion = ref('0%')
const seesionId = ref(uni.getStorageSync('seesionId') || '')
const seesionId = ref('')
const counts = ref({})
const login = (value) => {
hasLogin.value = true;
@@ -128,6 +133,13 @@ const useUserStore = defineStore("user", () => {
seesionId.value = seesionIdVal
}
function getUserstatistics() {
$api.createRequest('/app/user/statistics').then((resData) => {
counts.value = resData.data;
});
}
// 导入
return {
hasLogin,
@@ -140,8 +152,12 @@ const useUserStore = defineStore("user", () => {
getUserResume,
initSeesionId,
seesionId,
Completion
Completion,
getUserstatistics,
counts
}
}, {
unistorage: true,
})
export default useUserStore;

View File

@@ -6,7 +6,7 @@ import {
ref,
toRaw
} from 'vue'
import baseDB from './BaseDBStore';
import baseDB from '@/utils/db.js';
import {
msg,
CloneDeep,

View File

@@ -0,0 +1,31 @@
## 0.1.22024-07-17
chore: 移除冗余的 typescript 依赖
## 0.1.12024-07-17
fix: 修复 createUnistorage 导出
## 0.1.02024-07-10
fix!: 更新 pinia 类型
## 0.0.212024-07-10
chore!: 继承 pinia-plugin-persistedstate
## 0.0.192024-01-18
fix: 重新构建,不需要默认参数
## 0.0.162023-05-06
fix: 修复全局 key 移除
## 0.0.142023-04-29
fix: 修复全局 global key 选项
## 0.0.122023-04-07
- fix: 修复类型错误
## 0.0.112023-03-22
- chore: ts 支持
## 0.0.72022-04-29
- 更新 README

View File

@@ -0,0 +1,112 @@
import * as pinia from 'pinia';
import { StateTree, PiniaPluginContext, PiniaPlugin } from 'pinia';
type Prettify<T> = {
[K in keyof T]: T[K];
};
type StorageLike = Pick<Storage, 'getItem' | 'setItem'>;
interface Serializer {
/**
* Serializes state into string before storing
* @default JSON.stringify
*/
serialize: (value: StateTree) => string;
/**
* Deserializes string into state before hydrating
* @default JSON.parse
*/
deserialize: (value: string) => StateTree;
}
interface PersistedStateOptions {
/**
* Storage key to use.
* @default $store.id
*/
key?: string | ((id: string) => string);
/**
* Where to store persisted state.
* @default localStorage
*/
storage?: StorageLike;
/**
* Dot-notation paths to partially save state. Saves everything if undefined.
* @default undefined
*/
paths?: Array<string>;
/**
* Customer serializer to serialize/deserialize state.
*/
serializer?: Serializer;
/**
* Hook called before state is hydrated from storage.
* @default null
*/
beforeRestore?: (context: PiniaPluginContext) => void;
/**
* Hook called after state is hydrated from storage.
* @default undefined
*/
afterRestore?: (context: PiniaPluginContext) => void;
/**
* Logs errors in console when enabled.
* @default false
*/
debug?: boolean;
}
type PersistedStateFactoryOptions = Prettify<Pick<PersistedStateOptions, 'storage' | 'serializer' | 'afterRestore' | 'beforeRestore' | 'debug'> & {
/**
* Global key generator, allows pre/postfixing store keys.
* @default storeKey => storeKey
*/
key?: (storeKey: string) => string;
/**
* Automatically persists all stores, opt-out individually.
* @default false
*/
auto?: boolean;
}>;
declare module 'pinia' {
interface DefineStoreOptionsBase<S extends StateTree, Store> {
/**
* Persists store in storage.
* @see https://prazdevs.github.io/pinia-plugin-persistedstate
*/
persist?: boolean | PersistedStateOptions | PersistedStateOptions[];
unistorage?: boolean | PersistedStateOptions | PersistedStateOptions[];
}
interface PiniaCustomProperties {
/**
* Rehydrates store from persisted state
* Warning: this is for advances usecases, make sure you know what you're doing.
* @see https://prazdevs.github.io/pinia-plugin-persistedstate/guide/advanced.html#forcing-the-rehydration
*/
$hydrate: (opts?: {
runHooks?: boolean;
}) => void;
/**
* Persists store into configured storage
* Warning: this is for advances usecases, make sure you know what you're doing.
* @see https://prazdevs.github.io/pinia-plugin-persistedstate/guide/advanced.html#forcing-the-persistence
*/
$persist: () => void;
}
}
/**
* Creates a pinia persistence plugin
* @param factoryOptions global persistence options
* @returns pinia plugin
*/
declare function createPersistedState(factoryOptions?: PersistedStateFactoryOptions): PiniaPlugin;
declare const _default: pinia.PiniaPlugin;
export { PersistedStateFactoryOptions, PersistedStateOptions, Serializer, StorageLike, createPersistedState, _default as default, createUnistorage };
/**
* Creates a pinia persistence plugin with uniapp
* @param factoryOptions global persistence options
* @returns pinia plugin
*/
declare function createUnistorage(factoryOptions?: PersistedStateFactoryOptions): PiniaPlugin;

View File

@@ -0,0 +1,162 @@
// src/normalize.ts
function isObject(v) {
return typeof v === "object" && v !== null;
}
function normalizeOptions(options, factoryOptions) {
options = isObject(options) ? options : /* @__PURE__ */ Object.create(null);
return new Proxy(options, {
get(target, key, receiver) {
if (key === "key")
return Reflect.get(target, key, receiver);
return Reflect.get(target, key, receiver) || Reflect.get(factoryOptions, key, receiver);
}
});
}
// src/pick.ts
function get(state, path) {
return path.reduce((obj, p) => {
return obj == null ? void 0 : obj[p];
}, state);
}
function set(state, path, val) {
return path.slice(0, -1).reduce((obj, p) => {
if (/^(__proto__)$/.test(p))
return {};
else
return obj[p] = obj[p] || {};
}, state)[path[path.length - 1]] = val, state;
}
function pick(baseState, paths) {
return paths.reduce((substate, path) => {
const pathArray = path.split(".");
return set(substate, pathArray, get(baseState, pathArray));
}, {});
}
// src/plugin.ts
function parsePersistence(factoryOptions, store) {
return (o) => {
var _a;
try {
const {
storage = localStorage,
beforeRestore = void 0,
afterRestore = void 0,
serializer = {
serialize: JSON.stringify,
deserialize: JSON.parse
},
key = store.$id,
paths = null,
debug = false
} = o;
return {
storage,
beforeRestore,
afterRestore,
serializer,
key: ((_a = factoryOptions.key) != null ? _a : (k) => k)(typeof key == "string" ? key : key(store.$id)),
paths,
debug
};
} catch (e) {
if (o.debug)
console.error("[pinia-plugin-persistedstate]", e);
return null;
}
};
}
function hydrateStore(store, { storage, serializer, key, debug }) {
try {
const fromStorage = storage == null ? void 0 : storage.getItem(key);
if (fromStorage)
store.$patch(serializer == null ? void 0 : serializer.deserialize(fromStorage));
} catch (e) {
if (debug)
console.error("[pinia-plugin-persistedstate]", e);
}
}
function persistState(state, { storage, serializer, key, paths, debug }) {
try {
const toStore = Array.isArray(paths) ? pick(state, paths) : state;
storage.setItem(key, serializer.serialize(toStore));
} catch (e) {
if (debug)
console.error("[pinia-plugin-persistedstate]", e);
}
}
function createPersistedState(factoryOptions = {}) {
return (context) => {
const { auto = false } = factoryOptions;
const {
options: { persist = auto },
store,
pinia
} = context;
if (!persist)
return;
if (!(store.$id in pinia.state.value)) {
const original_store = pinia._s.get(store.$id.replace("__hot:", ""));
if (original_store)
Promise.resolve().then(() => original_store.$persist());
return;
}
const persistences = (Array.isArray(persist) ? persist.map((p) => normalizeOptions(p, factoryOptions)) : [normalizeOptions(persist, factoryOptions)]).map(parsePersistence(factoryOptions, store)).filter(Boolean);
store.$persist = () => {
persistences.forEach((persistence) => {
persistState(store.$state, persistence);
});
};
store.$hydrate = ({ runHooks = true } = {}) => {
persistences.forEach((persistence) => {
const { beforeRestore, afterRestore } = persistence;
if (runHooks)
beforeRestore == null ? void 0 : beforeRestore(context);
hydrateStore(store, persistence);
if (runHooks)
afterRestore == null ? void 0 : afterRestore(context);
});
};
persistences.forEach((persistence) => {
const { beforeRestore, afterRestore } = persistence;
beforeRestore == null ? void 0 : beforeRestore(context);
hydrateStore(store, persistence);
afterRestore == null ? void 0 : afterRestore(context);
store.$subscribe(
(_mutation, state) => {
persistState(state, persistence);
},
{
detached: true
}
);
});
};
}
function createUnistorage(globalOptions = {}) {
const persistedState = createPersistedState({
storage: {
getItem(key) {
return uni.getStorageSync(key);
},
setItem(key, value) {
uni.setStorageSync(key, value);
}
},
serializer: {
deserialize: JSON.parse,
serialize: JSON.stringify
},
...globalOptions
});
return (ctx) => {
if (ctx.options.unistorage) {
ctx.options.persist = ctx.options.unistorage;
}
return persistedState(ctx);
};
}
export { createPersistedState, createUnistorage };

View File

@@ -0,0 +1,94 @@
{
"id": "pinia-plugin-unistorage",
"displayName": "pinia-plugin-unistorage",
"version": "0.1.2",
"description": "uniapp 下 pinia 的本地数据缓存插件",
"keywords": [
"pinia",
"uniapp",
"storage",
"pinia-plugin",
"persistence"
],
"type": "module",
"main": "./index.js",
"types": "./index.d.ts",
"exports": {
".": {
"import": "./index.js",
"types": "./index.d.ts"
}
},
"engines": {
"HBuilderX": "^3.4.7"
},
"dcloudext": {
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/pinia-plugin-unistorage",
"type": "sdk-js"
},
"uni_modules": {
"dependencies": [],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y",
"alipay": "n"
},
"client": {
"Vue": {
"vue2": "y",
"vue3": "y"
},
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y",
"钉钉": "y",
"快手": "y",
"飞书": "y",
"京东": "y"
},
"快应用": {
"华为": "y",
"联盟": "y"
}
}
}
}
}

View File

@@ -0,0 +1,226 @@
<div align="center">
<img width="200px" height="200px" src="https://gitee.com/dishait/pinia-plugin-unistorage/raw/main/static/favicon.png" />
<h1>pinia-plugin-unistorage</h1>
<p>uniapp 下 pinia 的本地数据缓存插件</p>
</div>
<br />
<br />
<div align="center">
<img width="100%" height="100%" src="https://gitee.com/dishait/pinia-plugin-unistorage/raw/main/static/pinia-plugin-unistorage.gif" />
</div>
<br />
<br />
## 引用
该插件是
[pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate)
`uniapp` 版本,如果你需要在纯 `vue` 或者 `nuxt` 项目中使用 `pinia`
的本地数据缓存,请使用
[pinia-plugin-persistedstate](https://github.com/prazdevs/pinia-plugin-persistedstate)。
<br />
<br />
## 动机
为了实现多端的更简单的全局本地数据缓存
<br />
<br />
## 组织 🦔
欢迎关注 **帝莎编程**
- [官网](http://dishaxy.dishait.cn/)
- [Gitee](https://gitee.com/dishait)
- [Github](https://github.com/dishait)
- [网易云课堂](https://study.163.com/provider/480000001892585/index.htm?share=2&shareId=480000001892585)
<br />
<br />
## 使用
### 安装
#### 1. `cli` 创建的 `uniapp` 项目
```shell
npm i pinia-plugin-unistorage -D
```
```js
// main.js
import { createSSRApp } from "vue";
import * as Pinia from "pinia";
import { createUnistorage } from "pinia-plugin-unistorage";
export function createApp() {
const app = createSSRApp(App);
const store = Pinia.createPinia();
// 关键代码 👇
store.use(createUnistorage());
app.use(store);
return {
app,
Pinia, // 此处必须将 Pinia 返回
};
}
```
<br />
#### 2. `hbuilderx` 创建的 `uniapp` 项目
直接插件市场安装后引入注册
```js
// main.js
import { createSSRApp } from "vue";
import * as Pinia from "pinia";
import { createUnistorage } from "./uni_modules/pinia-plugin-unistorage";
export function createApp() {
const app = createSSRApp(App);
const store = Pinia.createPinia();
// 关键代码 👇
store.use(createUnistorage());
app.use(store);
return {
app,
Pinia, // 此处必须将 Pinia 返回
};
}
```
### 基础
```js
import { defineStore } from "pinia";
export const useStore = defineStore("main", {
state() {
return {
someState: "hello pinia",
};
},
unistorage: true, // 开启后对 state 的数据读写都将持久化
});
```
或者 `setup` 语法也是支持的
```js
import { defineStore } from "pinia";
export const useStore = defineStore(
"main",
() => {
const someState = ref("hello pinia");
return { someState };
},
{
unistorage: true, // 开启后对 state 的数据读写都将持久化
},
);
```
<br />
### 选项
#### 钩子
```js
import { defineStore } from "pinia";
export const useStore = defineStore("main", {
state() {
return {
someState: "hello pinia",
};
},
unistorage: {
// 初始化恢复前触发
beforeRestore(ctx) {},
// 初始化恢复后触发
afterRestore(ctx) {},
},
});
```
<br />
#### 序列化
大多数情况下你并不需要了解该选项
```js
import { defineStore } from "pinia";
export const useStore = defineStore("main", {
state() {
return {
someState: "hello pinia",
};
},
unistorage: {
serializer: {
// 序列化,默认为 JSON.stringify
serialize(v) {
return JSON.stringify(v);
},
// 反序列化,默认为 JSON.parse
deserialize(v) {
return JSON.parse(v);
},
},
},
});
```
<br />
#### 其他
```js
import { defineStore } from "pinia";
export const useStore = defineStore("main", {
state() {
return {
foo: "foo",
nested: {
data: "nested pinia",
},
someState: "hello pinia",
};
},
unistorage: {
key: "foo", // 缓存的键,默认为该 store 的 id这里是 main,
paths: ["foo", "nested.data"], // 需要缓存的路径,这里设置 foo 和 nested 下的 data 会被缓存
},
});
```
<br />
<br />
## License
Made with [markthree](https://github.com/markthree)
Published under [MIT License](./LICENSE).

View File

@@ -0,0 +1,35 @@
import {
createPersistedState,
type PersistedStateFactoryOptions,
} from "pinia-plugin-persistedstate";
export * from "pinia-plugin-persistedstate";
export function createUnistorage(
globalOptions: PersistedStateFactoryOptions = {},
) {
const persistedState = createPersistedState({
storage: {
getItem(key) {
// @ts-ignore
return uni.getStorageSync(key);
},
setItem(key, value) {
// @ts-ignore
uni.setStorageSync(key, value);
},
},
serializer: {
deserialize: JSON.parse,
serialize: JSON.stringify,
},
...globalOptions,
});
// @ts-ignore
return (ctx) => {
if (ctx.options.unistorage) {
ctx.options.persist = ctx.options.unistorage;
}
return persistedState(ctx);
};
}

91
utils/db.js Normal file
View File

@@ -0,0 +1,91 @@
// BaseDBStore.js
import IndexedDBHelper from '@/common/IndexedDBHelper.js'
// import UniStorageHelper from '../common/UniStorageHelper'
import useChatGroupDBStore from '@/stores/userChatGroupStore'
import config from '@/config'
class BaseStore {
db = null
isDBReady = false
dbName = 'BrowsingHistory' // 'AppMainDB'
initPromise = null
constructor() {
this.initPromise = this.checkAndInitDB()
}
async getDB() {
if (!this.initPromise) {
this.initPromise = this.checkAndInitDB();
}
await this.initPromise; // 等待初始化完成
return this.db;
}
async checkAndInitDB() {
if (config.OnlyUseCachedDB) {
return this.initDB()
}
const localVersion = uni.getStorageSync('indexedDBVersion') || 1
console.log('DBVersion: ', localVersion, config.DBversion)
if (localVersion === config.DBversion) {
return this.initDB() // 🟢 记得加 return
} else {
console.log('清空本地数据库')
await this.clearDB() // 🟢 建议用 await
uni.setStorageSync('indexedDBVersion', config.DBversion);
return this.initDB(); // 🟢 记得加 return
}
}
initDB() {
// // #ifdef H5
this.db = new IndexedDBHelper(this.dbName, config.DBversion);
// // #endif
return 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
}]
},
{
name: 'api_cache',
keyPath: "cacheKey", // 使用 URL+参数 作为主键
indexes: []
}
]).then(async () => {
// 这里原来的逻辑保留
if (useChatGroupDBStore) {
useChatGroupDBStore().init()
}
this.isDBReady = true
return this.db;
});
}
async clearDB() {
return new Promise((resolve, rejetc) => {
new IndexedDBHelper().deleteDB(this.dbName).then(() => {
resolve()
})
})
}
}
const baseDB = new BaseStore()
export default baseDB

View File

@@ -1,14 +1,15 @@
import config from "@/config.js"
import {
sm2_Decrypt,
sm2_Encrypt
} from '@/common/globalFunction';
import useUserStore from '@/stores/useUserStore';
import {
sm2_Encrypt,
sm4Decrypt,
sm4Encrypt
} from '../common/globalFunction';
} from '@/common/globalFunction';
import IndexedDBHelper from '@/common/IndexedDBHelper';
import useUserStore from '@/stores/useUserStore';
import baseDB from '@/utils/db.js';
const CACHE_STORE_NAME = 'api_cache';
const needToEncrypt = [
["post", "/app/login"],
@@ -20,6 +21,51 @@ const needToEncrypt = [
["get", "/app/user/experience/list"]
]
/**
* 带缓存的请求方法
*/
export async function createRequestWithCache(url, data = {}, method = 'GET', loading = false, headers = {},
onCacheLoad = null) {
// 是分页接口的话, 只缓存第一页的数据
if (data.current && data.current > 1) {
return createRequest(url, data, method, loading, headers);
}
const cacheKey = `${method.toUpperCase()}:${url}:${JSON.stringify(data)}`;
baseDB.getDB().then(async (dbHelper) => {
try {
const cachedRecord = await dbHelper.get(CACHE_STORE_NAME, cacheKey);
if (cachedRecord && cachedRecord.response && typeof onCacheLoad === 'function') {
onCacheLoad(cachedRecord.response);
}
} catch (e) {
console.error('读取缓存失败', e);
}
});
// 3. 发起网络请求
try {
const networkResponse = await createRequest(url, data, method, loading, headers);
baseDB.getDB().then(async (dbHelper) => {
try {
await dbHelper.update(CACHE_STORE_NAME, {
cacheKey: cacheKey,
response: networkResponse,
timestamp: Date.now()
});
console.log('💾 [BaseDB] 缓存更新:', url);
} catch (e) {
console.error('更新缓存失败', e);
}
});
return networkResponse;
} catch (error) {
throw error;
}
}
/**
* @param url String请求的地址默认none
* @param data Object请求的参数默认{}
@@ -35,13 +81,16 @@ export function createRequest(url, data = {}, method = 'GET', loading = false, h
mask: true
})
}
let Authorization = ''
if (useUserStore().token) {
Authorization = `${useUserStore().token}`
}
let header = {
...headers
};
const userStore = useUserStore();
const token = userStore.token;
const header = headers || {};
header["Authorization"] = encodeURIComponent(Authorization);
if (token) {
// 确保 Authorization 不会被覆盖,且进行编码
header["Authorization"] = encodeURIComponent(token);
}
// ------------------------------------------------------------------
// 检查当前请求是否需要加密
@@ -88,10 +137,12 @@ export function createRequest(url, data = {}, method = 'GET', loading = false, h
resolve(resData.data)
return
}
uni.showToast({
title: msg,
icon: 'none'
})
if (msg) {
uni.showToast({
title: msg,
icon: 'none'
})
}
}
if (resData.data?.code === 401 || resData.data?.code === 402) {
useUserStore().logOut()