用户行为统计
Some checks are pending
Node CI / build (14.x, macOS-latest) (push) Waiting to run
Node CI / build (14.x, ubuntu-latest) (push) Waiting to run
Node CI / build (14.x, windows-latest) (push) Waiting to run
Node CI / build (16.x, macOS-latest) (push) Waiting to run
Node CI / build (16.x, ubuntu-latest) (push) Waiting to run
Node CI / build (16.x, windows-latest) (push) Waiting to run
coverage CI / build (push) Waiting to run
Node pnpm CI / build (16.x, macOS-latest) (push) Waiting to run
Node pnpm CI / build (16.x, ubuntu-latest) (push) Waiting to run
Node pnpm CI / build (16.x, windows-latest) (push) Waiting to run

This commit is contained in:
francis-fh
2026-07-24 20:58:01 +08:00
parent 279011e652
commit d653cd97d6
5 changed files with 137 additions and 4 deletions

View File

@@ -19,8 +19,8 @@ export type MapProps = {
open: boolean;
};
export const aMapConfig = {
key: '9cfc9370bd8a941951da1cea0308e9e3',
securityJsCode: '7b16386c7f744c3ca05595965f2b037f',
key: 'b79ea14cb17704ab1a1a32c12d72f634',
securityJsCode: '04795ef5b20919d34cadc2e33e01d609',
};
/** 石河子市地图默认中心点与行政区划编码 */

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useParams, history, useLocation, useSearchParams } from '@umijs/max';
import {
Card,
@@ -206,6 +206,55 @@ const JobDetailPage: React.FC = () => {
const [industryTree, setIndustryTree] = useState<any[]>([]);
const [complaintVisible, setComplaintVisible] = useState(false);
// 行为上报:浏览岗位时长统计
const enterTimeRef = useRef<number>(0);
const reportedRef = useRef<boolean>(false);
const jobIdRef = useRef<string>('');
// 页面进入/离开时上报浏览行为
useEffect(() => {
enterTimeRef.current = Date.now();
reportedRef.current = false;
jobIdRef.current = String(originalJobData?.jobId || originalJobData?.id || id || '');
const calcDuration = () =>
Math.max(1, Math.floor((Date.now() - enterTimeRef.current) / 1000));
const doReport = (durationSeconds: number) => {
if (reportedRef.current) return;
reportedRef.current = true;
const actorId = localStorage.getItem('appUserId');
const targetId = jobIdRef.current;
if (!actorId || !targetId || durationSeconds <= 0) return;
fetch('/api/cms/behavior/report', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
actorType: '1',
actorId: String(actorId),
targetType: '2',
targetId: String(targetId),
viewDuration: String(durationSeconds),
}),
keepalive: true,
}).catch(() => {});
};
const handleBeforeUnload = () => {
doReport(calcDuration());
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
doReport(calcDuration());
};
}, []);
const detailTagGroups = useMemo(
() => [
{ label: '薪资范围与构成', values: jobDetail.salaryComposition },

View File

@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from 'react';
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
Card,
Row,
@@ -216,6 +216,70 @@ const JobListPage: React.FC = () => {
const [complaintVisible, setComplaintVisible] = useState(false);
const selectedJobId = selectedJob?.jobId || selectedJob?.id;
// 行为上报:浏览岗位时长统计
const jobStartTimeRef = useRef<number>(0);
const behaviorReportedRef = useRef<boolean>(false);
const currentJobIdRef = useRef<string>('');
const reportJobBehavior = useCallback((jobId: string) => {
if (behaviorReportedRef.current) return;
behaviorReportedRef.current = true;
const actorId = localStorage.getItem('appUserId');
if (!actorId || !jobId) return;
const duration = Math.max(1, Math.floor((Date.now() - jobStartTimeRef.current) / 1000));
if (duration <= 0) return;
fetch('/api/cms/behavior/report', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
actorType: '1',
actorId: String(actorId),
targetType: '2',
targetId: String(jobId),
viewDuration: String(duration),
}),
keepalive: true,
}).catch(() => {});
}, []);
// 切换岗位时上报上一个岗位的浏览时长
useEffect(() => {
if (!isJobPortalLoggedIn()) return;
const newJobId = String(selectedJobId || '');
// 如果之前有浏览中的岗位,先上报
if (currentJobIdRef.current && currentJobIdRef.current !== newJobId) {
reportJobBehavior(currentJobIdRef.current);
}
// 重置计时器,开始计时新岗位
behaviorReportedRef.current = false;
jobStartTimeRef.current = Date.now();
currentJobIdRef.current = newJobId;
}, [selectedJobId, reportJobBehavior]);
// 页面离开时上报当前岗位的浏览时长
useEffect(() => {
const handleBeforeUnload = () => {
if (currentJobIdRef.current) {
reportJobBehavior(currentJobIdRef.current);
}
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
if (currentJobIdRef.current) {
reportJobBehavior(currentJobIdRef.current);
}
};
}, [reportJobBehavior]);
const selectedJobDetailTagGroups = useMemo(
() =>
[

View File

@@ -66,6 +66,7 @@ export type ListFormProps = {
welfareBenefitsEnum: DictValueEnumObj;
workScheduleEnum: DictValueEnumObj;
keyPopulationsEnum: DictValueEnumObj;
jobFeatureEnum: DictValueEnumObj;
mode?: 'view' | 'edit' | 'create';
};
@@ -98,6 +99,7 @@ const listEdit: React.FC<ListFormProps> = (props) => {
welfareBenefitsEnum,
workScheduleEnum,
keyPopulationsEnum,
jobFeatureEnum,
} = props;
const { mode = props.values ? 'edit' : 'create' } = props;
const [showKeyPopulations, setShowKeyPopulations] = useState(false);
@@ -269,6 +271,7 @@ const listEdit: React.FC<ListFormProps> = (props) => {
}),
),
jobCategory: values.jobCategory,
jobFeature: values.jobFeature,
isUrgent: values.isUrgent ? 1 : 0,
isKeyPopulations: showKeyPopulations ? '0' : '1',
keyPopulations: showKeyPopulations ? values.keyPopulations : undefined,
@@ -355,6 +358,11 @@ const listEdit: React.FC<ListFormProps> = (props) => {
<ProDescriptions.Item dataIndex="jobLocation" label="工作地点" />
<ProDescriptions.Item dataIndex="jobType" label="区域" valueEnum={jobTypeEnum} />
<ProDescriptions.Item dataIndex="jobCategory" label="岗位标签" />
<ProDescriptions.Item
dataIndex="jobFeature"
label="岗位特征"
valueEnum={jobFeatureEnum}
/>
<ProDescriptions.Item
dataIndex="description"
label="岗位描述"
@@ -689,6 +697,13 @@ const listEdit: React.FC<ListFormProps> = (props) => {
style={{ width: 328 }}
/>
</ProForm.Item>
<ProFormSelect
width="md"
name="jobFeature"
label="岗位特征"
valueEnum={jobFeatureEnum}
placeholder="请选择岗位特征"
/>
<ProFormText width="md" name="jobLocation" label="工作地点" placeholder="请输入工作地点" />
<ProForm.Item label="位置选择" colProps={{ span: 12 }}>
<div>

View File

@@ -98,6 +98,7 @@ function ManagementList() {
const [welfareBenefitsEnum, setWelfareBenefitsEnum] = useState<any>([]);
const [workScheduleEnum, setWorkScheduleEnum] = useState<any>([]);
const [keyPopulationsEnum, setKeyPopulationsEnum] = useState<any>([]);
const [jobFeatureEnum, setJobFeatureEnum] = useState<any>([]);
const [currentRow, setCurrentRow] = useState<API.ManagementList.Manage>();
const [modalVisible, setModalVisible] = useState<boolean>(false);
const [mode, setMode] = useState<'view' | 'edit' | 'create'>('create');
@@ -132,6 +133,9 @@ function ManagementList() {
getDictValueEnum('kp_type', false, true).then((data) => {
setKeyPopulationsEnum(data);
});
getDictValueEnum('job_feature', false, true).then((data) => {
setJobFeatureEnum(data);
});
}, []);
async function changeRelease(record: API.ManagementList.Manage) {
@@ -541,6 +545,7 @@ function ManagementList() {
welfareBenefitsEnum={welfareBenefitsEnum}
workScheduleEnum={workScheduleEnum}
keyPopulationsEnum={keyPopulationsEnum}
jobFeatureEnum={jobFeatureEnum}
></EditManageRow>
</Fragment>
);