Browse Source

Merge branch 'feature_moreStaDimen' of Web/contentCooper into master

jihuaqiang 2 weeks ago
parent
commit
74b5b8682a

+ 22 - 0
src/utils/helper.ts

@@ -14,4 +14,26 @@ export function getRandomKey(length=16) {
         result += chars[randomIndex]
     }
     return result
+}
+
+interface OptionItem {
+  label: string;
+  value: string | number;
+}
+
+/**
+ * 将枚举类型转换为选项数组
+ * @param enumObj 枚举对象
+ * @returns 选项数组,label 为枚举的键,value 为枚举的值
+ * @example
+ * enum Status { Active = '活跃', Inactive = '未激活' }
+ * enumToOptions(Status) // [{ label: 'Active', value: '活跃' }, { label: 'Inactive', value: '未激活' }]
+ */
+export function enumToOptions(enumObj: Record<string, any>): OptionItem[] {
+  return Object.entries(enumObj)
+    .filter(([key]) => !isNaN(Number(key)) ? false : true)
+    .map(([key, value]) => ({
+      label: key,
+      value: value
+    }));
 }

+ 29 - 6
src/views/publishContent/weCom/components/videoSelectModal/index.tsx

@@ -16,7 +16,8 @@ import { VideoListResponse } from '@src/views/publishContent/weGZH/components/ty
 import http from '@src/http';
 import { getVideoContentListApi } from '@src/http/api';
 import { useVideoCategoryOptions } from '@src/views/publishContent/weGZH/hooks/useVideoCategoryOptions';
-import { WeComPlanType, WeVideoItem } from '@src/views/publishContent/weCom/type'
+import { WeComPlanType, WeVideoItem, VideoSearchPlanType } from '@src/views/publishContent/weCom/type'
+import { enumToOptions } from '@src/utils/helper';
 
 const { Text, Paragraph } = Typography;
 
@@ -28,9 +29,16 @@ interface VideoSelectModalProps {
 	initialSelectedIds?: number[];
 }
 
-const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, onOk, initialSelectedIds = [] }) => {
+export enum VideoSortType {
+	平台推荐 = 0,
+	行业裂变率 = 1,
+	本渠道传播率 = 2,
+}
+
+const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, onOk, planType, initialSelectedIds = [] }) => {
 	const { videoCategoryOptions } = useVideoCategoryOptions();
 	const [category, setCategory] = useState<string>();
+	const [sort, setSort] = useState<VideoSortType>(VideoSortType.平台推荐);
 	const [searchTerm, setSearchTerm] = useState<string>('');
 	const [currentPage, setCurrentPage] = useState(1);
 	const [pageSize, setPageSize] = useState(10);
@@ -49,6 +57,8 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 		const res = await http.post<VideoListResponse>(getVideoContentListApi, {
 			category,
 			title: searchTerm,
+			sort,
+			type: planType === WeComPlanType.社群 ? VideoSearchPlanType.企微社群 : VideoSearchPlanType.企微自动回复,
 			pageNum: pageNum || currentPage,
 			pageSize: _pageSize || pageSize,
 		}).catch(() => {
@@ -171,11 +181,20 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 				}
 			>
 				<div className="flex flex-wrap gap-4 mb-6">
+					<div className="flex items-center gap-2">
+						<span className="text-gray-600">排序选项:</span>
+						<Select
+							style={{ width: 120 }}
+							value={sort}
+							onChange={setSort}
+							options={enumToOptions(VideoSortType)}
+						/>
+					</div>
 					<div className="flex items-center gap-2">
 						<span className="text-gray-600">品类:</span>
 						<Select
 							placeholder="选择品类"
-							style={{ width: 180 }}
+							style={{ width: 160 }}
 							value={category}
 							allowClear
 							onChange={setCategory}
@@ -186,7 +205,7 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 						<span className="text-gray-600">视频标题:</span>
 						<Input
 							placeholder="搜索视频标题"
-							style={{ width: 200 }}
+							style={{ width: 180 }}
 							value={searchTerm}
 							allowClear
 							onChange={e => setSearchTerm(e.target.value)}
@@ -222,8 +241,12 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 									</div>
 								</div>
 								<div className="p-3 flex justify-between items-center">
-									<Text type="secondary" className="text-xs">传播效率: {video.score?.toFixed(2)}</Text>
-									{isSelected ? (
+									<div className="flex flex-col gap-1">
+										<Text type="secondary" className="text-xs">平台传播得分: {video.score?.toFixed(2) || '无'}</Text>
+										<Text type="secondary" className="text-xs">行业裂变率: {video.industryFissionRate?.toFixed(2) || '无'}</Text>
+										<Text type="secondary" className="text-xs">本渠道传播率: {video.channelFissionRate?.toFixed(2) || '无'}</Text>
+									</div>
+								{isSelected ? (
 										<CheckCircleFilled className="text-green-500 text-xl" />
 									) : (
 										<div className={`w-5 h-5 border-2 ${isDisabled ? 'border-gray-200 bg-gray-100' : 'border-gray-300' } rounded-full`}></div>

+ 9 - 0
src/views/publishContent/weCom/type.ts

@@ -19,6 +19,13 @@ export enum WeComPlanType {
 	社群 = '0',
 }
 
+export enum VideoSearchPlanType {
+	自动回复 = 0,
+	服务号推送 = 1,
+	企微社群 = 2,
+	企微自动回复 = 3,
+}
+
 export interface WeVideoItem {
 	videoId: number;
 	video: string;
@@ -26,6 +33,8 @@ export interface WeVideoItem {
 	cover: string;
 	score: number,
 	scene?: 0 | 1;
+	industryFissionRate?: number;
+	channelFissionRate?: number;
 }
 
 export interface AddWeComPlanParam {

+ 1 - 0
src/views/publishContent/weGZH/components/publishPlanModal/index.tsx

@@ -307,6 +307,7 @@ const AddPunlishPlanModal: React.FC<AddPunlishPlanModalProps> = ({
 
 			{/* Video Selection Drawer */}
 			<VideoSelectModal
+				planType={type}
 				visible={isVideoSelectVisible}
 				onClose={handleVideoSelectionCancel}
 				onOk={handleVideoSelectionOk}

+ 2 - 0
src/views/publishContent/weGZH/components/types.ts

@@ -8,6 +8,8 @@ export interface VideoItem {
 	pageUrl: string,
 	video: string,
 	videoId: number,
+	industryFissionRate: number,
+	channelFissionRate: number,
 } 
 
 export interface VideoListResponse {

+ 25 - 4
src/views/publishContent/weGZH/components/videoSelectModal/index.tsx

@@ -16,10 +16,15 @@ import { VideoItem, VideoListResponse } from '../types';
 import http from '@src/http';
 import { getVideoContentListApi } from '@src/http/api';
 import { useVideoCategoryOptions } from '../../hooks/useVideoCategoryOptions';
+import { VideoSortType } from '@src/views/publishContent/weCom/components/videoSelectModal';
+import { GzhPlanType } from '../../hooks/useGzhPlanList';
+import { VideoSearchPlanType } from '@src/views/publishContent/weCom/type';
+import { enumToOptions } from '@src/utils/helper';
 
 const { Paragraph, Text } = Typography;
 
 interface VideoSelectModalProps {
+	planType: GzhPlanType;
 	visible: boolean;
 	onClose: () => void;
 	onOk: (selectedVideos: VideoItem[]) => void;
@@ -27,9 +32,10 @@ interface VideoSelectModalProps {
 	selectedVideos?: VideoItem[];
 }
 
-const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, onOk, initialSelectedIds = [], selectedVideos = [] }) => {
+const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ planType, visible, onClose, onOk, initialSelectedIds = [], selectedVideos = [] }) => {
 	const { videoCategoryOptions } = useVideoCategoryOptions();
 	const [category, setCategory] = useState<string>();
+	const [sort, setSort] = useState<VideoSortType>(VideoSortType.平台推荐);
 	const [searchTerm, setSearchTerm] = useState<string>('');
 	const [currentPage, setCurrentPage] = useState(1);
 	const [pageSize, setPageSize] = useState(10);
@@ -48,6 +54,8 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 		const res = await http.post<VideoListResponse>(getVideoContentListApi, {
 			category,
 			title: searchTerm,
+			sort,
+			type: planType === GzhPlanType.自动回复 ? VideoSearchPlanType.自动回复 : VideoSearchPlanType.服务号推送,
 			pageNum: pageNum || currentPage,
 			pageSize: _pageSize || pageSize,
 		}).catch(() => {
@@ -148,11 +156,20 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 				}
 			>
 				<div className="flex flex-wrap gap-4 mb-6">
+					<div className="flex items-center gap-2">
+						<span className="text-gray-600">排序选项:</span>
+						<Select
+							style={{ width: 120 }}
+							value={sort}
+							onChange={setSort}
+							options={enumToOptions(VideoSortType)}
+						/>
+					</div>
 					<div className="flex items-center gap-2">
 						<span className="text-gray-600">品类:</span>
 						<Select
 							placeholder="选择品类"
-							style={{ width: 180 }}
+							style={{ width: 160 }}
 							value={category}
 							allowClear
 							onChange={setCategory}
@@ -163,7 +180,7 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 						<span className="text-gray-600">视频标题:</span>
 						<Input
 							placeholder="搜索视频标题"
-							style={{ width: 200 }}
+							style={{ width: 180 }}
 							value={searchTerm}
 							onPressEnter={handleSearch}
 							allowClear
@@ -199,7 +216,11 @@ const VideoSelectModal: React.FC<VideoSelectModalProps> = ({ visible, onClose, o
 									</div>
 								</div>
 								<div className="p-3 flex justify-between items-center">
-									<Text type="secondary" className="text-xs">传播效率: {video.score?.toFixed(2)}</Text>
+									<div className="flex flex-col gap-1">
+										<Text type="secondary" className="text-xs">平台传播得分: {video.score?.toFixed(2) || '无'}</Text>
+										<Text type="secondary" className="text-xs">行业裂变率: {video.industryFissionRate?.toFixed(2) || '无'}</Text>
+										<Text type="secondary" className="text-xs">本渠道传播率: {video.channelFissionRate?.toFixed(2) || '无'}</Text>
+									</div>
 									{isSelected ? (
 										<CheckCircleFilled className="text-green-500 text-xl" />
 									) : (

+ 2 - 2
src/views/weData/gzh/index.tsx

@@ -63,7 +63,7 @@ const Gzh: React.FC = () => {
       render: (text) => `${(Number(text) * 100).toFixed(2)}%`
     },
     {
-      title: '传播得分',
+      title: '本渠道裂变率',
       dataIndex: 'score',
       key: 'score',
     },
@@ -97,7 +97,7 @@ const Gzh: React.FC = () => {
       render: (text) => `${(Number(text) * 100).toFixed(2)}%`
     },
     {
-      title: '传播得分',
+      title: '本渠道裂变率',
       dataIndex: 'score',
       key: 'score',
     },

+ 19 - 1
src/views/weData/qw/index.tsx

@@ -54,6 +54,24 @@ const Qw: React.FC = () => {
       title: '传播得分',
       dataIndex: 'score',
       key: 'score',
+    },
+	];
+	
+	const totalColumns:TableProps['columns'] = [
+    {
+      title: '日期',
+      dataIndex: 'dateStr',
+      key: 'dateStr',
+    },
+    {
+      title: '小程序访问人数',
+      dataIndex: 'firstLevel',
+      key: 'firstLevel',
+    },
+    {
+      title: '本渠道裂变率',
+      dataIndex: 'score',
+      key: 'score',
     },
   ];
 
@@ -199,7 +217,7 @@ const Qw: React.FC = () => {
             children: (
               <Table
                 dataSource={dataSource}
-                columns={columns}
+                columns={totalColumns}
                 rowKey="dateStr"
                 loading={loading}
                 pagination={{