用户行为统计
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
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:
@@ -19,8 +19,8 @@ export type MapProps = {
|
||||
open: boolean;
|
||||
};
|
||||
export const aMapConfig = {
|
||||
key: '9cfc9370bd8a941951da1cea0308e9e3',
|
||||
securityJsCode: '7b16386c7f744c3ca05595965f2b037f',
|
||||
key: 'b79ea14cb17704ab1a1a32c12d72f634',
|
||||
securityJsCode: '04795ef5b20919d34cadc2e33e01d609',
|
||||
};
|
||||
|
||||
/** 石河子市地图默认中心点与行政区划编码 */
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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(
|
||||
() =>
|
||||
[
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user