mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-01-19 13:13:22 +08:00
Refactor: Make page worker as a top module
This commit is contained in:
parent
ded0770aea
commit
95afadbec5
@ -5,9 +5,12 @@ defineProps<{ model: AmazonDetailItem }>();
|
||||
<template>
|
||||
<div class="detail-description">
|
||||
<n-descriptions label-placement="left" bordered :column="4" label-style="min-width: 100px">
|
||||
<n-descriptions-item label="ASIN" :span="2">
|
||||
<n-descriptions-item label="ASIN">
|
||||
{{ model.asin }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="获取日期">
|
||||
{{ model.timestamp }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="评价">
|
||||
{{ model.rating || '-' }}
|
||||
</n-descriptions-item>
|
||||
|
||||
0
src/composables/useCloudCommit.ts
Normal file
0
src/composables/useCloudCommit.ts
Normal file
@ -1,4 +0,0 @@
|
||||
import amazon from './amazon';
|
||||
import homedepot from './homedepot';
|
||||
|
||||
export { amazon, homedepot };
|
||||
@ -84,7 +84,7 @@ const columns: TableColumn[] = [
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
title: '创建日期',
|
||||
title: '获取日期',
|
||||
key: 'createTime',
|
||||
minWidth: 160,
|
||||
},
|
||||
@ -146,6 +146,7 @@ const extraHeaders: Header<AmazonItem>[] = [
|
||||
{ prop: 'category1.rank', label: '大类排行' },
|
||||
{ prop: 'category2.name', label: '小类' },
|
||||
{ prop: 'category2.rank', label: '小类排行' },
|
||||
{ prop: 'timestamp', label: '获取日期(详情页)' },
|
||||
{
|
||||
prop: 'imageUrls',
|
||||
label: '商品图片链接',
|
||||
|
||||
@ -50,7 +50,7 @@ const columns: TableColumn[] = [
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
key: 'actions',
|
||||
render(row: (typeof allItems.value)[0]) {
|
||||
return (
|
||||
<n-space>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { AmazonPageWorker, AmazonPageWorkerEvents, LanchTaskBaseOptions } from './types';
|
||||
import type { Tabs } from 'webextension-polyfill';
|
||||
import { withErrorHandling } from '../error-handler';
|
||||
import { withErrorHandling } from './error-handler';
|
||||
import {
|
||||
AmazonDetailPageInjector,
|
||||
AmazonReviewPageInjector,
|
||||
@ -1,6 +1,6 @@
|
||||
import type { HomedepotEvents, HomedepotWorker, LanchTaskBaseOptions } from './types';
|
||||
import { Tabs } from 'webextension-polyfill';
|
||||
import { withErrorHandling } from '../error-handler';
|
||||
import { withErrorHandling } from './error-handler';
|
||||
import { HomedepotDetailPageInjector } from '~/logic/web-injectors/homedepot';
|
||||
import { BaseWorker } from './base';
|
||||
|
||||
@ -58,7 +58,7 @@ class HomedepotWorkerImpl
|
||||
}
|
||||
|
||||
export default {
|
||||
useHomedepotWorker() {
|
||||
getHomedepotWorker() {
|
||||
return HomedepotWorkerImpl.getInstance();
|
||||
},
|
||||
};
|
||||
@ -1,6 +1,7 @@
|
||||
import { amazon } from '~/logic/page-worker';
|
||||
import amazon from './amazon';
|
||||
import homedepot from './homedepot';
|
||||
import { uploadImage } from '~/logic/upload';
|
||||
import { useLongTask } from './useLongTask';
|
||||
import { useLongTask } from '~/composables/useLongTask';
|
||||
|
||||
export interface AmazonPageWorkerSettings {
|
||||
searchItems?: Ref<AmazonSearchItem[]>;
|
||||
@ -9,7 +10,7 @@ export interface AmazonPageWorkerSettings {
|
||||
commitChangeIngerval?: number;
|
||||
}
|
||||
|
||||
class PageWorkerFactory {
|
||||
class AmazonPageWorkerFactory {
|
||||
public amazonWorker: ReturnType<typeof this.buildAmazonPageWorker> | null = null;
|
||||
|
||||
public amazonWorkerSettings: AmazonPageWorkerSettings = {};
|
||||
@ -160,11 +161,112 @@ class PageWorkerFactory {
|
||||
}
|
||||
}
|
||||
|
||||
const facotry = new PageWorkerFactory();
|
||||
|
||||
export function usePageWorker(type: 'amazon', settings?: AmazonPageWorkerSettings) {
|
||||
if (type === 'amazon') {
|
||||
return facotry.loadAmazonPageWorker(settings);
|
||||
}
|
||||
throw new Error(`Unsupported page worker type: ${type}`);
|
||||
export interface HomedepotWorkerSettings {
|
||||
detailItems?: Ref<Map<string, HomedepotDetailItem>>;
|
||||
commitChangeIngerval?: number;
|
||||
}
|
||||
|
||||
class HomedepotWorkerFactory {
|
||||
public homedepotWorkerSettings: HomedepotWorkerSettings = {};
|
||||
|
||||
public homedepotWorker: ReturnType<typeof this.buildHomedepotWorker> | null = null;
|
||||
|
||||
public buildHomedepotWorker() {
|
||||
const worker = homedepot.getHomedepotWorker();
|
||||
const { isRunning, startTask } = useLongTask();
|
||||
|
||||
const detailCache = new Map<string, HomedepotDetailItem>();
|
||||
|
||||
const unsubscribeFuncs = [] as (() => void)[];
|
||||
|
||||
onMounted(() => {
|
||||
unsubscribeFuncs.push(
|
||||
...[
|
||||
worker.on('error', () => {
|
||||
worker.stop();
|
||||
}),
|
||||
worker.on('detail-item-collected', (ev) => {
|
||||
const { item } = ev;
|
||||
if (detailCache.has(item.OSMID)) {
|
||||
const origin = detailCache.get(item.OSMID);
|
||||
detailCache.set(item.OSMID, { ...origin, ...item });
|
||||
} else {
|
||||
detailCache.set(item.OSMID, item);
|
||||
}
|
||||
}),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
unsubscribeFuncs.forEach((unsubscribe) => unsubscribe());
|
||||
unsubscribeFuncs.splice(0, unsubscribeFuncs.length);
|
||||
});
|
||||
|
||||
const commitChange = () => {
|
||||
const { detailItems } = this.homedepotWorkerSettings;
|
||||
if (typeof detailItems !== 'undefined') {
|
||||
for (const [k, v] of detailCache.entries()) {
|
||||
detailItems.value.delete(k); // Trigger update
|
||||
detailItems.value.set(k, v);
|
||||
}
|
||||
detailCache.clear();
|
||||
}
|
||||
};
|
||||
|
||||
const taskWrapper = <T extends (...params: any) => any>(func: T) => {
|
||||
const { commitChangeIngerval = 1500 } = this.homedepotWorkerSettings;
|
||||
return (...params: Parameters<T>) =>
|
||||
startTask(async () => {
|
||||
const interval = setInterval(() => commitChange(), commitChangeIngerval);
|
||||
await func(...params);
|
||||
clearInterval(interval);
|
||||
commitChange();
|
||||
});
|
||||
};
|
||||
|
||||
const runDetailPageTask = taskWrapper(worker.runDetailPageTask.bind(worker));
|
||||
|
||||
return {
|
||||
isRunning,
|
||||
runDetailPageTask,
|
||||
on: worker.on.bind(worker),
|
||||
off: worker.off.bind(worker),
|
||||
once: worker.once.bind(worker),
|
||||
stop: worker.stop.bind(worker),
|
||||
};
|
||||
}
|
||||
|
||||
loadHomedepotWorker(settings?: HomedepotWorkerSettings) {
|
||||
if (settings) {
|
||||
this.homedepotWorkerSettings = { ...this.homedepotWorkerSettings, ...settings };
|
||||
}
|
||||
if (!this.homedepotWorker) {
|
||||
this.homedepotWorker = this.buildHomedepotWorker();
|
||||
}
|
||||
return this.homedepotWorker;
|
||||
}
|
||||
}
|
||||
|
||||
const amazonfacotry = new AmazonPageWorkerFactory();
|
||||
|
||||
const homedepotfactory = new HomedepotWorkerFactory();
|
||||
|
||||
export function usePageWorker(
|
||||
type: 'amazon',
|
||||
settings?: AmazonPageWorkerSettings,
|
||||
): ReturnType<typeof amazonfacotry.loadAmazonPageWorker>;
|
||||
export function usePageWorker(
|
||||
type: 'homedepot',
|
||||
settings?: HomedepotWorkerSettings,
|
||||
): ReturnType<typeof homedepotfactory.loadHomedepotWorker>;
|
||||
export function usePageWorker(type: 'amazon' | 'homedepot', settings?: any) {
|
||||
switch (type) {
|
||||
case 'amazon':
|
||||
return amazonfacotry.loadAmazonPageWorker(settings);
|
||||
case 'homedepot':
|
||||
return homedepotfactory.loadHomedepotWorker(settings);
|
||||
default:
|
||||
throw new Error(`Unsupported page worker type: ${type}`);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { Timeline } from '~/components/ProgressReport.vue';
|
||||
import { usePageWorker } from '~/composables/usePageWorker';
|
||||
import { usePageWorker } from '~/page-worker';
|
||||
import { detailAsinInput, detailItems, detailWorkerSettings } from '~/logic/storages/amazon';
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script lang="ts" setup>
|
||||
import type { Timeline } from '~/components/ProgressReport.vue';
|
||||
import { usePageWorker } from '~/composables/usePageWorker';
|
||||
import { usePageWorker } from '~/page-worker';
|
||||
import { reviewAsinInput, reviewItems } from '~/logic/storages/amazon';
|
||||
|
||||
const worker = usePageWorker('amazon', { reviewItems });
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { keywordsList } from '~/logic/storages/amazon';
|
||||
import { searchItems } from '~/logic/storages/amazon';
|
||||
import type { Timeline } from '~/components/ProgressReport.vue';
|
||||
import { usePageWorker } from '~/composables/usePageWorker';
|
||||
import { usePageWorker } from '~/page-worker';
|
||||
|
||||
const message = useMessage();
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import DetailPageEntry from './AmazonEntries/DetailPageEntry.vue';
|
||||
import SearchPageEntry from './AmazonEntries/SearchPageEntry.vue';
|
||||
import ReviewPageEntry from './AmazonEntries/ReviewPageEntry.vue';
|
||||
import { usePageWorker } from '~/composables/usePageWorker';
|
||||
import { usePageWorker } from '~/page-worker';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
|
||||
@ -1,48 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import type { Timeline } from '~/components/ProgressReport.vue';
|
||||
import { useLongTask } from '~/composables/useLongTask';
|
||||
import { homedepot } from '~/logic/page-worker';
|
||||
import { usePageWorker } from '~/page-worker';
|
||||
import { detailItems } from '~/logic/storages/homedepot';
|
||||
|
||||
const inputText = ref('');
|
||||
const { isRunning, startTask } = useLongTask();
|
||||
const idInputRef = useTemplateRef('id-input');
|
||||
|
||||
const worker = homedepot.useHomedepotWorker();
|
||||
worker.channel.on('detail-item-collected', ({ item }) => {
|
||||
const worker = usePageWorker('homedepot', { detailItems });
|
||||
worker.on('detail-item-collected', ({ item }) => {
|
||||
timelines.value.push({
|
||||
type: 'success',
|
||||
title: `成功`,
|
||||
content: `成功获取到${item.OSMID}的商品信息`,
|
||||
time: new Date().toLocaleString(),
|
||||
});
|
||||
detailItems.value.set(item.OSMID, item);
|
||||
});
|
||||
|
||||
const timelines = ref<Timeline[]>([]);
|
||||
|
||||
const handleStart = () =>
|
||||
startTask(async () => {
|
||||
timelines.value.push({
|
||||
type: 'info',
|
||||
title: `开始`,
|
||||
content: '任务开始',
|
||||
time: new Date().toLocaleString(),
|
||||
});
|
||||
await worker.runDetailPageTask(
|
||||
inputText.value.split('\n').filter((id) => /\d+/.exec(id)),
|
||||
const handleStart = async () => {
|
||||
idInputRef.value?.validate().then(async (success) => {
|
||||
if (success) {
|
||||
const ids = inputText.value.split(/\n|\s|,|;/).filter((item) => item.length > 0);
|
||||
timelines.value = [
|
||||
{
|
||||
type: 'info',
|
||||
title: '开始',
|
||||
time: new Date().toLocaleString(),
|
||||
content: `开始采集OSMID: ${ids.join(', ')}`,
|
||||
},
|
||||
];
|
||||
await worker.runDetailPageTask(ids, {
|
||||
progress: (remains) => {
|
||||
inputText.value = remains.join('\n');
|
||||
},
|
||||
},
|
||||
);
|
||||
if (remains.length > 0) {
|
||||
timelines.value.push({
|
||||
type: 'info',
|
||||
title: `结束`,
|
||||
content: '任务完成',
|
||||
title: '继续',
|
||||
time: new Date().toLocaleString(),
|
||||
content: `继续采集OSMID: ${remains.join(', ')}`,
|
||||
});
|
||||
inputText.value = remains.join('\n');
|
||||
}
|
||||
},
|
||||
});
|
||||
timelines.value.push({
|
||||
type: 'info',
|
||||
title: '结束',
|
||||
time: new Date().toLocaleString(),
|
||||
content: `数据采集完成`,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleInterrupt = () => {
|
||||
worker.stop();
|
||||
@ -55,13 +64,19 @@ const handleInterrupt = () => {
|
||||
<div class="interative-section">
|
||||
<ids-input
|
||||
v-model="inputText"
|
||||
:disabled="isRunning"
|
||||
ref="asin-input"
|
||||
:disabled="worker.isRunning.value"
|
||||
ref="id-input"
|
||||
:match-pattern="/^\d+(\n\d+)*\n?$/g"
|
||||
placeholder="输入OSMID"
|
||||
validate-message="请输入格式正确的OSMID"
|
||||
/>
|
||||
<n-button v-if="!isRunning" round size="large" type="primary" @click="handleStart">
|
||||
<n-button
|
||||
v-if="!worker.isRunning.value"
|
||||
round
|
||||
size="large"
|
||||
type="primary"
|
||||
@click="handleStart"
|
||||
>
|
||||
<template #icon>
|
||||
<ant-design-thunderbolt-outlined />
|
||||
</template>
|
||||
@ -74,7 +89,7 @@ const handleInterrupt = () => {
|
||||
停止
|
||||
</n-button>
|
||||
</div>
|
||||
<div v-if="isRunning" class="running-tip-section">
|
||||
<div v-if="worker.isRunning.value" class="running-tip-section">
|
||||
<n-alert title="Warning" type="warning"> 警告,在插件运行期间请勿与浏览器交互。 </n-alert>
|
||||
</div>
|
||||
<progress-report class="progress-report" :timelines="timelines" />
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user