达人工作台完善
This commit is contained in:
@@ -21,6 +21,22 @@
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-end gap-4">
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-xs font-bold text-slate-400 uppercase tracking-wider">国家</label>
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-xl border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700">
|
||||
<span>{{ countryData || '未识别' }}</span>
|
||||
<span class="rounded bg-slate-100 px-2 py-0.5 text-xs text-slate-500">{{ regionCode || '--' }}</span>
|
||||
<button @click="editCountry" :disabled="isRunning || isRefreshingCountry"
|
||||
class="rounded-md p-1 text-slate-500 transition-colors hover:bg-slate-100 disabled:opacity-50">
|
||||
<span class="material-icons-round text-base">edit</span>
|
||||
</button>
|
||||
<button @click="refreshCountryFn" :disabled="isRunning || isRefreshingCountry"
|
||||
class="rounded-md p-1 text-slate-500 transition-colors hover:bg-slate-100 disabled:opacity-50">
|
||||
<span class="material-icons-round text-base" :class="{ 'animate-spin': isRefreshingCountry }">refresh</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<label class="text-xs font-bold text-slate-400 uppercase tracking-wider">最小粉丝数</label>
|
||||
<el-input v-model="crawlForm.fansMin" placeholder="最小值" style="width: 150px" type="number"
|
||||
@@ -33,9 +49,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700">
|
||||
当前仓库里还没有达人爬虫的后端启动接口,这里先保留前端配置和任务态,等后端接口接入后再把参数真正传下去。
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="rounded-2xl border border-slate-100 bg-white p-5">
|
||||
@@ -119,6 +132,9 @@ import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { getUser } from '@/utils/storage';
|
||||
import { expertInfoPage } from '@/api/account';
|
||||
import { useCountryInfo } from '@/composables/useCountryInfo';
|
||||
import { CountryCode } from '@/utils/countryUtil';
|
||||
import { usePythonBridge } from '@/utils/pythonBridge';
|
||||
|
||||
const userInfo = ref({});
|
||||
const tenantId = ref('');
|
||||
@@ -135,11 +151,14 @@ const tableData = ref([]);
|
||||
const isRunning = ref(false);
|
||||
const elapsedSeconds = ref(0);
|
||||
const elapsedTimerId = ref(null);
|
||||
const { countryData, isLoading: isRefreshingCountry, initCountryInfo, refreshCountry, showEditCountryDialog } = useCountryInfo();
|
||||
const { controlExpertCapture } = usePythonBridge();
|
||||
|
||||
const stats = computed(() => ({
|
||||
total: total.value,
|
||||
pageCount: tableData.value.length,
|
||||
}));
|
||||
const regionCode = computed(() => resolveRegionCode(countryData.value));
|
||||
|
||||
const formattedElapsed = computed(() => {
|
||||
const hours = Math.floor(elapsedSeconds.value / 3600);
|
||||
@@ -152,6 +171,7 @@ onMounted(() => {
|
||||
userInfo.value = getUser() || {};
|
||||
tenantId.value = String(userInfo.value?.tenantId || '');
|
||||
queryDate.value = buildTodayText();
|
||||
void initCountryInfo((key) => key);
|
||||
void loadList();
|
||||
});
|
||||
|
||||
@@ -176,6 +196,15 @@ function normalizeNumber(value) {
|
||||
return Number.isFinite(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
function resolveRegionCode(countryName) {
|
||||
if (!countryName) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const matched = Object.entries(CountryCode).find(([code, name]) => code.length === 2 && name === countryName);
|
||||
return matched?.[0] || '';
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) {
|
||||
return '';
|
||||
@@ -229,9 +258,23 @@ function validateCrawlRange() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!regionCode.value) {
|
||||
ElMessage.error('当前国家无法转换为地区代码,请先修改国家');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function buildCapturePayload(isStart) {
|
||||
return {
|
||||
region: regionCode.value,
|
||||
MinFollowerCount: normalizeNumber(crawlForm.value.fansMin) ?? 0,
|
||||
MaxFollowerCount: normalizeNumber(crawlForm.value.fansMax) ?? 0,
|
||||
isStart,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
if (!tenantId.value) {
|
||||
tableData.value = [];
|
||||
@@ -270,7 +313,7 @@ function stopElapsedTimer() {
|
||||
}
|
||||
}
|
||||
|
||||
function handleStart() {
|
||||
async function handleStart() {
|
||||
if (!tenantId.value) {
|
||||
ElMessage.error('缺少 tenantId,无法启动达人工作台');
|
||||
return;
|
||||
@@ -280,17 +323,41 @@ function handleStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await controlExpertCapture(JSON.stringify(buildCapturePayload(true)));
|
||||
if (!result?.success) {
|
||||
ElMessage.error(result?.error || '启动达人爬虫失败');
|
||||
return;
|
||||
}
|
||||
|
||||
isRunning.value = true;
|
||||
startElapsedTimer();
|
||||
ElMessage.success('达人工作台已进入运行态');
|
||||
ElMessage.success('达人工作台已开始运行');
|
||||
}
|
||||
|
||||
function handleStop() {
|
||||
async function handleStop() {
|
||||
const result = await controlExpertCapture(JSON.stringify(buildCapturePayload(false)));
|
||||
if (!result?.success) {
|
||||
ElMessage.error(result?.error || '停止达人爬虫失败');
|
||||
return;
|
||||
}
|
||||
|
||||
isRunning.value = false;
|
||||
stopElapsedTimer();
|
||||
ElMessage.success('达人工作台已停止');
|
||||
}
|
||||
|
||||
async function refreshCountryFn() {
|
||||
await refreshCountry((key) => key);
|
||||
}
|
||||
|
||||
async function editCountry() {
|
||||
try {
|
||||
await showEditCountryDialog((key) => key);
|
||||
} catch {
|
||||
// 用户取消
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSearch() {
|
||||
page.value = 1;
|
||||
await loadList();
|
||||
|
||||
Reference in New Issue
Block a user