mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-01-19 13:13:22 +08:00
Update UI & Worker
This commit is contained in:
parent
6662bba2b1
commit
3734c83d21
@ -6,7 +6,7 @@ const props = defineProps<{ model: AmazonDetailItem }>();
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="detail-description">
|
<div class="detail-description">
|
||||||
<n-descriptions label-placement="left" bordered :column="4">
|
<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" :span="2">
|
||||||
{{ props.model.asin }}
|
{{ props.model.asin }}
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
@ -28,11 +28,19 @@ const props = defineProps<{ model: AmazonDetailItem }>();
|
|||||||
<n-descriptions-item label="排名">
|
<n-descriptions-item label="排名">
|
||||||
{{ props.model.category2?.rank || '-' }}
|
{{ props.model.category2?.rank || '-' }}
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
<n-descriptions-item label="图片链接">
|
<n-descriptions-item label="图片链接" :span="4">
|
||||||
<div v-for="link in props.model.imageUrls">
|
<div v-for="link in props.model.imageUrls">
|
||||||
{{ link }}
|
{{ link }}
|
||||||
</div>
|
</div>
|
||||||
</n-descriptions-item>
|
</n-descriptions-item>
|
||||||
|
<n-descriptions-item label="评论" :span="2">
|
||||||
|
<div v-for="review in props.model.topReviews" style="margin-bottom: 5px">
|
||||||
|
<h5 style="margin: 0">{{ review.username }}:</h5>
|
||||||
|
<div v-for="paragraph in review.content.split('\n')">
|
||||||
|
{{ paragraph }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</n-descriptions-item>
|
||||||
</n-descriptions>
|
</n-descriptions>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
const openOptionsPage = async () => {
|
|
||||||
await browser.runtime.openOptionsPage();
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="header-menu">
|
|
||||||
<n-button class="setting-button" round @click="openOptionsPage" size="small">
|
|
||||||
<template #icon>
|
|
||||||
<n-icon size="18" color="#0f0f0f">
|
|
||||||
<stash:search-results />
|
|
||||||
</n-icon>
|
|
||||||
</template>
|
|
||||||
<template #default> 数据 </template>
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.header-menu {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row-reverse;
|
|
||||||
justify-content: flex-start;
|
|
||||||
|
|
||||||
.setting-button {
|
|
||||||
margin-right: 20px;
|
|
||||||
opacity: 0.7;
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
52
src/components/HeaderTitle.vue
Normal file
52
src/components/HeaderTitle.vue
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
const openOptionsPage = async () => {
|
||||||
|
await browser.runtime.openOptionsPage();
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="header-title">
|
||||||
|
<div class="header-menu">
|
||||||
|
<n-button class="setting-button" round @click="openOptionsPage" size="small">
|
||||||
|
<template #icon>
|
||||||
|
<n-icon size="18" color="#0f0f0f">
|
||||||
|
<stash:search-results />
|
||||||
|
</n-icon>
|
||||||
|
</template>
|
||||||
|
<template #default> 数据 </template>
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
<n-space class="app-title">
|
||||||
|
<mdi-cat style="font-size: 60px; color: black" />
|
||||||
|
<h1><slot></slot></h1>
|
||||||
|
</n-space>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.header-title {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-menu {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.setting-button {
|
||||||
|
margin-right: 20px;
|
||||||
|
opacity: 0.7;
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-title {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -177,11 +177,12 @@ class AmazonPageWorkerImpl implements AmazonPageWorker {
|
|||||||
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait 2 seconds.
|
await new Promise((resolve) => setTimeout(resolve, 2000)); // Wait 2 seconds.
|
||||||
//#endregion
|
//#endregion
|
||||||
//#region Fetch Top Reviews
|
//#region Fetch Top Reviews
|
||||||
// const reviews = await injector.getReviews();
|
const reviews = await injector.getTopReviews();
|
||||||
// reviews.length > 0 &&
|
reviews.length > 0 &&
|
||||||
// this.channel.emit('item-top-reviews-collected', {
|
this.channel.emit('item-top-reviews-collected', {
|
||||||
// reviews: reviews.map((r) => ({ asin: params.asin, ...r })),
|
asin: params.asin,
|
||||||
// });
|
topReviews: reviews.map((r) => ({ asin: params.asin, ...r })),
|
||||||
|
});
|
||||||
//#endregion
|
//#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
src/logic/page-worker/types.d.ts
vendored
2
src/logic/page-worker/types.d.ts
vendored
@ -57,7 +57,7 @@ interface AmazonPageWorkerEvents {
|
|||||||
/**
|
/**
|
||||||
* The event is fired when top reviews collected
|
* The event is fired when top reviews collected
|
||||||
*/
|
*/
|
||||||
['item-top-reviews-collected']: { reviews: AmazonReview[] };
|
['item-top-reviews-collected']: Pick<AmazonDetailItem, 'asin' | 'topReviews'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error event that occurs when there is an issue with the Amazon page worker.
|
* Error event that occurs when there is an issue with the Amazon page worker.
|
||||||
|
|||||||
@ -45,7 +45,7 @@ worker.channel.on('item-rating-collected', (ev) => {
|
|||||||
time: new Date().toLocaleString(),
|
time: new Date().toLocaleString(),
|
||||||
content: `评分: ${ev.rating};评价数:${ev.ratingCount}`,
|
content: `评分: ${ev.rating};评价数:${ev.ratingCount}`,
|
||||||
});
|
});
|
||||||
createOrUpdateDetailItem(ev);
|
updateDetailItems(ev);
|
||||||
});
|
});
|
||||||
worker.channel.on('item-category-rank-collected', (ev) => {
|
worker.channel.on('item-category-rank-collected', (ev) => {
|
||||||
timelines.value.push({
|
timelines.value.push({
|
||||||
@ -57,7 +57,7 @@ worker.channel.on('item-category-rank-collected', (ev) => {
|
|||||||
ev.category2 ? `#${ev.category2.rank} in ${ev.category2.name}` : '',
|
ev.category2 ? `#${ev.category2.rank} in ${ev.category2.name}` : '',
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
});
|
});
|
||||||
createOrUpdateDetailItem(ev);
|
updateDetailItems(ev);
|
||||||
});
|
});
|
||||||
worker.channel.on('item-images-collected', (ev) => {
|
worker.channel.on('item-images-collected', (ev) => {
|
||||||
timelines.value.push({
|
timelines.value.push({
|
||||||
@ -66,7 +66,16 @@ worker.channel.on('item-images-collected', (ev) => {
|
|||||||
time: new Date().toLocaleString(),
|
time: new Date().toLocaleString(),
|
||||||
content: `图片数: ${ev.imageUrls!.length}`,
|
content: `图片数: ${ev.imageUrls!.length}`,
|
||||||
});
|
});
|
||||||
createOrUpdateDetailItem(ev);
|
updateDetailItems(ev);
|
||||||
|
});
|
||||||
|
worker.channel.on('item-top-reviews-collected', (ev) => {
|
||||||
|
timelines.value.push({
|
||||||
|
type: 'success',
|
||||||
|
title: `商品${ev.asin}精选评论`,
|
||||||
|
time: new Date().toLocaleString(),
|
||||||
|
content: `精选评论数: ${ev.topReviews!.length}`,
|
||||||
|
});
|
||||||
|
updateDetailItems(ev);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleImportAsin: UploadOnChange = ({ fileList }) => {
|
const handleImportAsin: UploadOnChange = ({ fileList }) => {
|
||||||
@ -139,7 +148,7 @@ const handleInterrupt = () => {
|
|||||||
message.info('已触发中断,正在等待当前任务完成。', { duration: 2000 });
|
message.info('已触发中断,正在等待当前任务完成。', { duration: 2000 });
|
||||||
};
|
};
|
||||||
|
|
||||||
const createOrUpdateDetailItem = (info: AmazonDetailItem) => {
|
const updateDetailItems = (info: AmazonDetailItem) => {
|
||||||
const targetIndex = detailItems.value.findLastIndex((item) => info.asin === item.asin);
|
const targetIndex = detailItems.value.findLastIndex((item) => info.asin === item.asin);
|
||||||
if (targetIndex > -1) {
|
if (targetIndex > -1) {
|
||||||
const origin = detailItems.value[targetIndex];
|
const origin = detailItems.value[targetIndex];
|
||||||
@ -152,12 +161,8 @@ const createOrUpdateDetailItem = (info: AmazonDetailItem) => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="detail-page-worker">
|
<div class="detail-page-entry">
|
||||||
<header-menu />
|
<header-title>Detail Page</header-title>
|
||||||
<div class="title">
|
|
||||||
<mdi-cat style="color: black; font-size: 60px" />
|
|
||||||
<h1 style="font-size: 30px; color: black">Detail Page</h1>
|
|
||||||
</div>
|
|
||||||
<div class="interative-section">
|
<div class="interative-section">
|
||||||
<n-space>
|
<n-space>
|
||||||
<n-upload @change="handleImportAsin" accept=".txt" :max="1">
|
<n-upload @change="handleImportAsin" accept=".txt" :max="1">
|
||||||
@ -210,24 +215,13 @@ const createOrUpdateDetailItem = (info: AmazonDetailItem) => {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.detail-page-worker {
|
.detail-page-entry {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: 20px 0 30px 0;
|
|
||||||
font-size: 60px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interative-section {
|
.interative-section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
18
src/sidepanel/ReviewPageEntry.vue
Normal file
18
src/sidepanel/ReviewPageEntry.vue
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts" setup></script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="review-page-entry">
|
||||||
|
<header-title>Review Page</header-title>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.review-page-entry {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -82,12 +82,8 @@ const handleInterrupt = () => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="search-page-worker">
|
<div class="search-page-entry">
|
||||||
<header-menu />
|
<header-title>Search Page</header-title>
|
||||||
<n-space class="app-title">
|
|
||||||
<mdi-cat style="font-size: 60px; color: black" />
|
|
||||||
<h1>Search Page</h1>
|
|
||||||
</n-space>
|
|
||||||
<div class="interactive-section">
|
<div class="interactive-section">
|
||||||
<n-dynamic-input
|
<n-dynamic-input
|
||||||
:disabled="running"
|
:disabled="running"
|
||||||
@ -121,11 +117,11 @@ const handleInterrupt = () => {
|
|||||||
<n-alert title="Warning" type="warning"> 警告,在插件运行期间请勿与浏览器交互。 </n-alert>
|
<n-alert title="Warning" type="warning"> 警告,在插件运行期间请勿与浏览器交互。 </n-alert>
|
||||||
</div>
|
</div>
|
||||||
<progress-report class="progress-report" :timelines="timelines" />
|
<progress-report class="progress-report" :timelines="timelines" />
|
||||||
</main>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.search-page-worker {
|
.search-page-entry {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -133,10 +129,6 @@ const handleInterrupt = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
.app-title {
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.interactive-section {
|
.interactive-section {
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
width: 80%;
|
width: 80%;
|
||||||
@ -1,15 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DetailPageWorker from './DetailPageWorker.vue';
|
import DetailPageEntry from './DetailPageEntry.vue';
|
||||||
import SearchPageWorker from './SearchPageWorker.vue';
|
import SearchPageEntry from './SearchPageEntry.vue';
|
||||||
|
import ReviewPageEntry from './ReviewPageEntry.vue';
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
name: '搜索页',
|
name: '搜索页',
|
||||||
component: SearchPageWorker,
|
component: SearchPageEntry,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '详情页',
|
name: '详情页',
|
||||||
component: DetailPageWorker,
|
component: DetailPageEntry,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '评论页',
|
||||||
|
component: ReviewPageEntry,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const selectedTab = ref(tabs[0].name);
|
const selectedTab = ref(tabs[0].name);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user