mirror of
https://github.com/primedigitaltech/azon_seeker.git
synced 2026-01-19 13:13:22 +08:00
feature: split 2 patterns of search page & declare wanderDetailPage function
This commit is contained in:
parent
94fa61dfa1
commit
1dc76ffc20
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,3 +15,6 @@ node_modules
|
|||||||
src/auto-imports.d.ts
|
src/auto-imports.d.ts
|
||||||
src/components.d.ts
|
src/components.d.ts
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
|
**/test_data.ts
|
||||||
|
**/TestPanel.vue
|
||||||
@ -1,3 +1,4 @@
|
|||||||
{
|
{
|
||||||
"singleQuote": true
|
"singleQuote": true,
|
||||||
|
"printWidth": 100
|
||||||
}
|
}
|
||||||
|
|||||||
8
.vscode/extensions.json
vendored
8
.vscode/extensions.json
vendored
@ -1,9 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": ["vue.volar"]
|
||||||
"vue.volar"
|
|
||||||
// "antfu.iconify",
|
|
||||||
// "antfu.unocss",
|
|
||||||
// "dbaeumer.vscode-eslint",
|
|
||||||
// "csstools.postcss"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
16
.vscode/launch.json
vendored
Normal file
16
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "msedge",
|
||||||
|
"request": "attach",
|
||||||
|
"name": "Attach to side panel",
|
||||||
|
"webRoot": "${workspaceFolder}/src/",
|
||||||
|
"port": 9222,
|
||||||
|
"urlFilter": "chrome-extension://*"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -1,12 +1,11 @@
|
|||||||
{
|
{
|
||||||
"cSpell.words": ["Vitesse"],
|
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"vite.autoStart": false,
|
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.css": "postcss"
|
"*.css": "postcss"
|
||||||
},
|
},
|
||||||
"prettier.tabWidth": 2
|
"prettier.tabWidth": 2,
|
||||||
|
"prettier.printWidth": 100
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/logic/execute-script.ts
Normal file
17
src/logic/execute-script.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param tabId
|
||||||
|
* @param func
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function executeScript<T>(tabId: number, func: () => Promise<T>): Promise<T | null> {
|
||||||
|
const injectResults = await browser.scripting.executeScript({
|
||||||
|
target: { tabId },
|
||||||
|
func,
|
||||||
|
});
|
||||||
|
const ret = injectResults.pop();
|
||||||
|
if (ret?.error) {
|
||||||
|
console.error('注入脚本时发生错误', ret.error);
|
||||||
|
}
|
||||||
|
return ret?.result as T | null;
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
import Emittery from 'emittery';
|
import Emittery from 'emittery';
|
||||||
import { AmazonPageWorker, AmazonPageWorkerEvents } from './types';
|
import type { AmazonGoodsLinkItem, AmazonPageWorker, AmazonPageWorkerEvents } from './types';
|
||||||
|
import Browser from 'webextension-polyfill';
|
||||||
|
import { executeScript } from '../execute-script';
|
||||||
|
|
||||||
class AmazonPageWorkerImpl implements AmazonPageWorker {
|
class AmazonPageWorkerImpl implements AmazonPageWorker {
|
||||||
readonly channel = new Emittery<AmazonPageWorkerEvents>();
|
readonly channel = new Emittery<AmazonPageWorkerEvents>();
|
||||||
@ -12,70 +14,123 @@ class AmazonPageWorkerImpl implements AmazonPageWorker {
|
|||||||
.query({ active: true, currentWindow: true })
|
.query({ active: true, currentWindow: true })
|
||||||
.then((tabs) => tabs[0]);
|
.then((tabs) => tabs[0]);
|
||||||
const currentUrl = new URL(tab.url!);
|
const currentUrl = new URL(tab.url!);
|
||||||
if (
|
if (currentUrl.hostname !== url.hostname || currentUrl.searchParams.get('k') !== keywords) {
|
||||||
currentUrl.hostname !== url.hostname ||
|
|
||||||
currentUrl.searchParams.get('k') !== keywords
|
|
||||||
) {
|
|
||||||
await browser.tabs.update(tab.id, { url: url.toString() });
|
await browser.tabs.update(tab.id, { url: url.toString() });
|
||||||
|
await new Promise<void>((resolve) => setTimeout(resolve, 1000));
|
||||||
}
|
}
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async wanderSearchSinglePage() {
|
private async wanderSearchSinglePage(tab: Browser.Tabs.Tab) {
|
||||||
const tab = await browser.tabs
|
const tabId = tab.id!;
|
||||||
.query({ active: true, currentWindow: true })
|
// #region Wait for the Next button to appear, indicating that the product items have finished loading
|
||||||
.then((tabs) => tabs[0]);
|
await executeScript(tabId, async () => {
|
||||||
const results = await browser.scripting.executeScript({
|
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(500 * Math.random())));
|
||||||
target: { tabId: tab.id! },
|
|
||||||
func: async () => {
|
|
||||||
try {
|
|
||||||
await new Promise((resolve) =>
|
|
||||||
setTimeout(resolve, 500 + ~~(500 * Math.random())),
|
|
||||||
);
|
|
||||||
while (!document.querySelector('.s-pagination-strip')) {
|
while (!document.querySelector('.s-pagination-strip')) {
|
||||||
window.scrollBy(0, ~~(Math.random() * 500) + 500);
|
window.scrollBy(0, ~~(Math.random() * 500) + 500);
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
}
|
}
|
||||||
const items = document.querySelectorAll<HTMLDivElement>(
|
});
|
||||||
|
// #endregion
|
||||||
|
// #region Determine the type of product search page https://github.com/primedigitaltech/azon_seeker/issues/1
|
||||||
|
const pagePattern = await executeScript(tabId, async () => {
|
||||||
|
return [
|
||||||
|
...(document.querySelectorAll<HTMLDivElement>(
|
||||||
'.a-section.a-spacing-small.puis-padding-left-small',
|
'.a-section.a-spacing-small.puis-padding-left-small',
|
||||||
);
|
) as unknown as HTMLDivElement[]),
|
||||||
const links: string[] = [];
|
].filter((e) => e.getClientRects().length > 0).length === 0
|
||||||
items.forEach((el) => {
|
? 'pattern-1'
|
||||||
const link =
|
: 'pattern-2';
|
||||||
el.querySelector<HTMLAnchorElement>('a.a-link-normal')?.href;
|
|
||||||
link && links.push(link);
|
|
||||||
});
|
});
|
||||||
const nextButton =
|
if (typeof pagePattern !== 'string') {
|
||||||
document.querySelector<HTMLLinkElement>('.s-pagination-next');
|
this.channel.emit('error', { message: '无法判断商品搜索页类型', url: tab.url });
|
||||||
if (
|
throw new Error('无法判断商品搜索页类型');
|
||||||
nextButton &&
|
}
|
||||||
!nextButton.classList.contains('s-pagination-disabled')
|
// #endregion
|
||||||
) {
|
// #region Retrieve key nodes and their information from the critical product search page
|
||||||
await new Promise((resolve) =>
|
let data: AmazonGoodsLinkItem[] | null = null;
|
||||||
setTimeout(resolve, 500 + ~~(500 * Math.random())),
|
switch (pagePattern) {
|
||||||
);
|
// 处理商品以列表形式展示的情况
|
||||||
|
case 'pattern-1':
|
||||||
|
data = await executeScript(tabId, async () => {
|
||||||
|
const items = [
|
||||||
|
...(document.querySelectorAll<HTMLDivElement>(
|
||||||
|
'.a-section.a-spacing-small.a-spacing-top-small:not(.a-text-right)',
|
||||||
|
) as unknown as HTMLDivElement[]),
|
||||||
|
].filter((e) => e.getClientRects().length > 0);
|
||||||
|
const linkObjs = items.reduce<AmazonGoodsLinkItem[]>((objs, el) => {
|
||||||
|
const link = el.querySelector<HTMLAnchorElement>('a')?.href;
|
||||||
|
const title = el
|
||||||
|
.querySelector<HTMLHeadingElement>('h2.a-color-base')
|
||||||
|
?.getAttribute('aria-label');
|
||||||
|
link && objs.push({ link, title: title || '' });
|
||||||
|
return objs;
|
||||||
|
}, []);
|
||||||
|
return linkObjs;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
// 处理商品以二维图片格展示的情况
|
||||||
|
case 'pattern-2':
|
||||||
|
data = await executeScript(tabId, async () => {
|
||||||
|
const items = [
|
||||||
|
...(document.querySelectorAll<HTMLDivElement>(
|
||||||
|
'.a-section.a-spacing-small.puis-padding-left-small',
|
||||||
|
) as unknown as HTMLDivElement[]),
|
||||||
|
].filter((e) => e.getClientRects().length > 0);
|
||||||
|
const linkObjs = items.reduce<AmazonGoodsLinkItem[]>((objs, el) => {
|
||||||
|
const link = el.querySelector<HTMLAnchorElement>('a.a-link-normal')?.href;
|
||||||
|
const title = el.querySelector<HTMLHeadingElement>('h2.a-color-base')?.innerText;
|
||||||
|
link && objs.push({ link, title: title || '' });
|
||||||
|
return objs;
|
||||||
|
}, []);
|
||||||
|
return linkObjs;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
// #region Determine if it is the last page, otherwise navigate to the next page
|
||||||
|
const hasNextPage = await executeScript(tabId, async () => {
|
||||||
|
const nextButton = document.querySelector<HTMLLinkElement>('.s-pagination-next');
|
||||||
|
if (nextButton) {
|
||||||
|
if (!nextButton.classList.contains('s-pagination-disabled')) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 500 + ~~(500 * Math.random())));
|
||||||
nextButton.click();
|
nextButton.click();
|
||||||
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
return links;
|
} else {
|
||||||
} catch (e) {
|
throw new Error('Error: next page button not found');
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
// #endregion
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
return results.pop()?.result as string[] | null;
|
if (data === null || typeof hasNextPage !== 'boolean') {
|
||||||
|
this.channel.emit('error', { message: '爬取单页信息失败', url: tab.url });
|
||||||
|
throw new Error('爬取单页信息失败');
|
||||||
|
}
|
||||||
|
return { data, hasNextPage };
|
||||||
}
|
}
|
||||||
|
|
||||||
public async wanderSearchList(): Promise<void> {
|
public async wanderSearchList(): Promise<void> {
|
||||||
let links = await this.wanderSearchSinglePage();
|
const tab = await browser.tabs
|
||||||
while (links) {
|
.query({ active: true, currentWindow: true })
|
||||||
this.channel.emit('item-links-collected', { links });
|
.then((tabs) => tabs[0]);
|
||||||
links = await this.wanderSearchSinglePage();
|
let stopSignal = false;
|
||||||
|
let result = { hasNextPage: true, data: [] as AmazonGoodsLinkItem[] };
|
||||||
|
while (result.hasNextPage && !stopSignal) {
|
||||||
|
result = await this.wanderSearchSinglePage(tab);
|
||||||
|
this.channel.emit('item-links-collected', { objs: result.data });
|
||||||
|
this.channel.on('error', () => {
|
||||||
|
stopSignal = true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return new Promise((resolve) => setTimeout(resolve, 1000));
|
return new Promise((resolve) => setTimeout(resolve, 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async wanderDetailPage(): Promise<void> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PageWorkerFactory {
|
class PageWorkerFactory {
|
||||||
|
|||||||
23
src/logic/page-worker/types.d.ts
vendored
23
src/logic/page-worker/types.d.ts
vendored
@ -1,13 +1,18 @@
|
|||||||
import type Emittery from 'emittery';
|
import type Emittery from 'emittery';
|
||||||
|
|
||||||
|
type AmazonGoodsLinkItem = { link: string; title: string };
|
||||||
|
|
||||||
interface AmazonPageWorkerEvents {
|
interface AmazonPageWorkerEvents {
|
||||||
/**
|
/**
|
||||||
* Emitted when a new item is found on the Amazon page.
|
* This event is used to collect links to items on the Amazon search page.
|
||||||
* @param link - The item link that was found.
|
|
||||||
*/
|
*/
|
||||||
['item-links-collected']: { links: string[] };
|
['item-links-collected']: { objs: AmazonGoodsLinkItem[] };
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error event that occurs when there is an issue with the Amazon page worker.
|
||||||
|
*/
|
||||||
|
['error']: { message: string; url?: string };
|
||||||
|
}
|
||||||
|
|
||||||
interface AmazonPageWorker {
|
interface AmazonPageWorker {
|
||||||
/**
|
/**
|
||||||
@ -17,15 +22,19 @@ interface AmazonPageWorker {
|
|||||||
readonly channel: Emittery<AmazonPageWorkerEvents>;
|
readonly channel: Emittery<AmazonPageWorkerEvents>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search for a list of items on Amazon
|
* Search for a list of goods on Amazon
|
||||||
* @param keywords - The keywords to search for on Amazon.
|
* @param keywords - The keywords to search for on Amazon.
|
||||||
* @returns A promise that resolves to a string representing the search URL.
|
* @returns A promise that resolves to a string representing the search URL.
|
||||||
*/
|
*/
|
||||||
doSearch(keywords: string): Promise<string>;
|
doSearch(keywords: string): Promise<string>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Browsing item search page and collect links to those items.
|
* Browsing goods search page and collect links to those goods.
|
||||||
* @param entryUrl - The URL of the Amazon search page to start from.
|
|
||||||
*/
|
*/
|
||||||
wanderSearchList(): Promise<void>;
|
wanderSearchList(): Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Browsing goods detail page and collect target information.
|
||||||
|
*/
|
||||||
|
wanderDetailPage(): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/sidepanel/App.vue
Normal file
11
src/sidepanel/App.vue
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import Sidepanel from './Sidepanel.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-dialog-provider>
|
||||||
|
<n-message-provider>
|
||||||
|
<sidepanel />
|
||||||
|
</n-message-provider>
|
||||||
|
</n-dialog-provider>
|
||||||
|
</template>
|
||||||
@ -1,28 +1,85 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { keywords } from '~/logic/storage';
|
import { keywords } from '~/logic/storage';
|
||||||
import pageWorker from '~/logic/page-worker';
|
import pageWorker from '~/logic/page-worker';
|
||||||
|
import type { AmazonGoodsLinkItem } from '~/logic/page-worker/types';
|
||||||
|
import { NButton, type DataTableColumns } from 'naive-ui';
|
||||||
|
|
||||||
const links = ref<string[]>([]);
|
const message = useMessage();
|
||||||
const worker = pageWorker.createAmazonPageWorker();
|
const worker = pageWorker.createAmazonPageWorker();
|
||||||
|
|
||||||
|
type TableData = AmazonGoodsLinkItem & { rank: number };
|
||||||
|
|
||||||
|
const items = ref<AmazonGoodsLinkItem[]>([]);
|
||||||
|
const page = reactive({ current: 1, size: 5 });
|
||||||
|
const columns: DataTableColumns<TableData> = [
|
||||||
|
{
|
||||||
|
title: '排位',
|
||||||
|
key: 'rank',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标题',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '链接',
|
||||||
|
key: 'link',
|
||||||
|
render(row) {
|
||||||
|
return h(
|
||||||
|
NButton,
|
||||||
|
{
|
||||||
|
type: 'primary',
|
||||||
|
text: true,
|
||||||
|
size: 'small',
|
||||||
|
onClick: async () => {
|
||||||
|
const tab = await browser.tabs
|
||||||
|
.query({
|
||||||
|
active: true,
|
||||||
|
currentWindow: true,
|
||||||
|
})
|
||||||
|
.then((ts) => ts.pop());
|
||||||
|
if (tab) {
|
||||||
|
await browser.tabs.update(tab.id, {
|
||||||
|
url: row.link,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
() => '前往',
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const itemView = computed(() => {
|
||||||
|
const { current, size } = page;
|
||||||
|
return items.value
|
||||||
|
.slice((current - 1) * size, current * size)
|
||||||
|
.map((v, i) => ({ ...v, rank: 1 + (current - 1) * size + i }));
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
worker.channel.on('item-links-collected', (ev) => {
|
worker.channel.on('item-links-collected', (ev) => {
|
||||||
links.value = links.value.concat(ev.links);
|
items.value = items.value.concat(ev.objs);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSearch = async () => {
|
const onCollect = async () => {
|
||||||
if (keywords.value.trim() === '') {
|
if (keywords.value.trim() === '') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
message.info('开始收集');
|
||||||
|
worker.channel.on('error', ({ message: msg }) => {
|
||||||
|
message.error(msg);
|
||||||
|
});
|
||||||
await worker.doSearch(keywords.value);
|
await worker.doSearch(keywords.value);
|
||||||
await worker.wanderSearchList();
|
await worker.wanderSearchList();
|
||||||
|
message.info('完成');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<main class="side-panel">
|
<main class="side-panel">
|
||||||
<n-space>
|
<n-space class="app-header">
|
||||||
<mdi-cat style="font-size: 60px; color: black" />
|
<mdi-cat style="font-size: 60px; color: black" />
|
||||||
<h1>Azon Seeker</h1>
|
<h1>Azon Seeker</h1>
|
||||||
</n-space>
|
</n-space>
|
||||||
@ -35,22 +92,27 @@ const onSearch = async () => {
|
|||||||
round
|
round
|
||||||
placeholder="请输入关键词"
|
placeholder="请输入关键词"
|
||||||
/>
|
/>
|
||||||
<n-button round size="large" @click="onSearch">搜索</n-button>
|
<n-button type="primary" round size="large" @click="onCollect">采集</n-button>
|
||||||
</n-space>
|
</n-space>
|
||||||
<div style="height: 10px"></div>
|
<div style="height: 10px"></div>
|
||||||
<n-card class="result-content-container" title="结果框">
|
<n-card class="result-content-container" title="结果框">
|
||||||
<n-empty v-if="links.length === 0" description="还没有结果哦">
|
<n-empty v-if="items.length === 0" description="还没有结果哦">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<n-icon :size="50">
|
<n-icon :size="50">
|
||||||
<solar-cat-linear />
|
<solar-cat-linear />
|
||||||
</n-icon>
|
</n-icon>
|
||||||
</template>
|
</template>
|
||||||
</n-empty>
|
</n-empty>
|
||||||
<n-list size="medium" v-else>
|
<n-space vertical v-else>
|
||||||
<n-list-item v-for="(link, index) in links" :key="index">
|
<n-data-table :columns="columns" :data="itemView" />
|
||||||
<n-tag :href="link" target="_blank">{{ link }}</n-tag>
|
<n-pagination
|
||||||
</n-list-item>
|
v-model:page="page.current"
|
||||||
</n-list>
|
v-model:page-size="page.size"
|
||||||
|
:page-count="~~(items.length / page.size) + 1"
|
||||||
|
:page-sizes="[5, 10, 20]"
|
||||||
|
show-size-picker
|
||||||
|
/>
|
||||||
|
</n-space>
|
||||||
</n-card>
|
</n-card>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
@ -58,13 +120,16 @@ const onSearch = async () => {
|
|||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.side-panel {
|
.side-panel {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100vh;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
|
||||||
|
.app-header {
|
||||||
|
margin-top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
.search-input-box {
|
.search-input-box {
|
||||||
min-width: 270px;
|
min-width: 270px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import App from './Sidepanel.vue';
|
import App from './App.vue';
|
||||||
import { setupApp } from '~/logic/common-setup';
|
import { setupApp } from '~/logic/common-setup';
|
||||||
import '../styles';
|
import '../styles';
|
||||||
|
|
||||||
|
|||||||
@ -33,12 +33,7 @@ export const sharedConfig: UserConfig = {
|
|||||||
'webextension-polyfill': [['=', 'browser']],
|
'webextension-polyfill': [['=', 'browser']],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'naive-ui': [
|
'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
|
||||||
'useDialog',
|
|
||||||
'useMessage',
|
|
||||||
'useNotification',
|
|
||||||
'useLoadingBar',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
dts: r('src/auto-imports.d.ts'),
|
dts: r('src/auto-imports.d.ts'),
|
||||||
@ -54,6 +49,7 @@ export const sharedConfig: UserConfig = {
|
|||||||
IconsResolver({
|
IconsResolver({
|
||||||
prefix: '',
|
prefix: '',
|
||||||
}),
|
}),
|
||||||
|
// auto import naive ui
|
||||||
NaiveUiResolver(),
|
NaiveUiResolver(),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
@ -67,10 +63,7 @@ export const sharedConfig: UserConfig = {
|
|||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
apply: 'build',
|
apply: 'build',
|
||||||
transformIndexHtml(html, { path }) {
|
transformIndexHtml(html, { path }) {
|
||||||
return html.replace(
|
return html.replace(/"\/assets\//g, `"${relative(dirname(path), '/assets')}/`);
|
||||||
/"\/assets\//g,
|
|
||||||
`"${relative(dirname(path), '/assets')}/`,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user