feat(patrol-delete): 删除条件改为添加式交互——输入后回车/点添加逐个加入chips列表(可单项移除),去掉原'保存到条件库+勾选'两步式与已知状态下拉,启动时随任务携带条件
This commit is contained in:
@@ -63,51 +63,43 @@
|
||||
<div class="section-title condition-title">
|
||||
<span>删除条件</span>
|
||||
<span class="condition-title-hint">
|
||||
注意删除条件的描述需要和页面描述一致
|
||||
输入删除条件后点击添加(或回车);启动时作为本次任务的删除条件携带
|
||||
</span>
|
||||
</div>
|
||||
<div class="condition-panel">
|
||||
<div class="condition-input-row">
|
||||
<el-input
|
||||
v-model="conditionInput"
|
||||
v-model="conditionSelectValue"
|
||||
clearable
|
||||
placeholder="请输入删除条件"
|
||||
@keyup.enter="confirmAddCondition"
|
||||
placeholder="请输入删除条件,回车或点击添加"
|
||||
@keyup.enter="addCurrentCondition"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="opt-btn"
|
||||
:disabled="addingCondition"
|
||||
@click="confirmAddCondition"
|
||||
@click="addCurrentCondition"
|
||||
>
|
||||
{{ addingCondition ? "保存中..." : "保存" }}
|
||||
添加
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="!deleteConditions.length" class="empty-conditions">
|
||||
暂无删除条件,保存后可在启动任务时多选携带。
|
||||
<div v-if="!taskConditionTexts.length" class="empty-conditions">
|
||||
暂无删除条件,输入后点击添加。
|
||||
</div>
|
||||
<ul v-else class="condition-list">
|
||||
<ul v-else class="condition-list condition-list--chips">
|
||||
<li
|
||||
v-for="condition in deleteConditions"
|
||||
:key="condition.id"
|
||||
class="condition-item"
|
||||
v-for="text in taskConditionTexts"
|
||||
:key="text"
|
||||
class="condition-chip"
|
||||
:title="text"
|
||||
>
|
||||
<label class="condition-check">
|
||||
<input
|
||||
v-model="selectedConditionIds"
|
||||
type="checkbox"
|
||||
:value="condition.id"
|
||||
/>
|
||||
<span :title="condition.conditionText">
|
||||
{{ condition.conditionText }}
|
||||
</span>
|
||||
</label>
|
||||
<span class="condition-chip-text">{{ text }}</span>
|
||||
<button
|
||||
type="button"
|
||||
class="link-danger"
|
||||
@click="removeCondition(condition.id)"
|
||||
class="condition-chip-remove"
|
||||
title="移除"
|
||||
@click="removeTaskConditionText(text)"
|
||||
>
|
||||
删除
|
||||
×
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -247,22 +239,18 @@ import TaskCenterPanel from "@/shared/components/tasks/TaskCenterPanel.vue";
|
||||
import type { TaskItemView, TaskStatCard } from "@/shared/components/tasks/types";
|
||||
import {
|
||||
addPatrolDeleteCandidate,
|
||||
addPatrolDeleteCondition,
|
||||
createPatrolDeleteTask,
|
||||
deletePatrolDeleteCandidate,
|
||||
deletePatrolDeleteCondition,
|
||||
deletePatrolDeleteHistory,
|
||||
deletePatrolDeleteTask,
|
||||
getPatrolDeleteDashboard,
|
||||
getPatrolDeleteHistory,
|
||||
getPatrolDeleteTaskProgressBatch,
|
||||
getPatrolDeleteResultDownloadUrl,
|
||||
listPatrolDeleteConditions,
|
||||
listPatrolDeleteCandidates,
|
||||
matchPatrolDeleteShops,
|
||||
submitPatrolDeleteTaskResult,
|
||||
type PatrolDeleteCandidateVo,
|
||||
type PatrolDeleteConditionVo,
|
||||
type PatrolDeleteCartRatio,
|
||||
type PatrolDeleteCountrySection,
|
||||
type PatrolDeleteDashboardVo,
|
||||
@@ -289,11 +277,12 @@ const MAX_TRANSIENT_ERRORS = 30;
|
||||
const ziniaoVersion = useZiniaoVersion();
|
||||
|
||||
const shopInput = ref("");
|
||||
const conditionInput = ref("");
|
||||
const candidates = ref<PatrolDeleteCandidateVo[]>([]);
|
||||
const selectedCandidates = ref<PatrolDeleteCandidateVo[]>([]);
|
||||
const deleteConditions = ref<PatrolDeleteConditionVo[]>([]);
|
||||
const selectedConditionIds = ref<number[]>([]);
|
||||
/** 本次任务已添加的删除条件(文本;输入后回车/点添加逐个加入,启动时随任务携带) */
|
||||
const taskConditionTexts = ref<string[]>([]);
|
||||
/** 删除条件输入框当前值 */
|
||||
const conditionSelectValue = ref("");
|
||||
const selectedCountryCodes = ref<string[]>([...EU_COUNTRY_CODES]);
|
||||
const matchedItems = ref<PatrolDeleteShopQueueItem[]>([]);
|
||||
const historyItems = ref<PatrolDeleteHistoryItem[]>([]);
|
||||
@@ -304,7 +293,6 @@ const dashboard = ref<PatrolDeleteDashboardVo>({
|
||||
failedTaskCount: 0,
|
||||
});
|
||||
const adding = ref(false);
|
||||
const addingCondition = ref(false);
|
||||
const matching = ref(false);
|
||||
const pushing = ref(false);
|
||||
const queuePushResult = ref("");
|
||||
@@ -731,14 +719,6 @@ async function loadCandidates() {
|
||||
candidates.value = await listPatrolDeleteCandidates();
|
||||
}
|
||||
|
||||
async function loadConditions() {
|
||||
deleteConditions.value = await listPatrolDeleteConditions();
|
||||
const existingIds = new Set(deleteConditions.value.map((item) => item.id));
|
||||
selectedConditionIds.value = selectedConditionIds.value.filter((id) =>
|
||||
existingIds.has(id),
|
||||
);
|
||||
}
|
||||
|
||||
async function loadDashboard() {
|
||||
dashboard.value = await getPatrolDeleteDashboard();
|
||||
}
|
||||
@@ -848,39 +828,27 @@ async function confirmAdd() {
|
||||
}
|
||||
}
|
||||
|
||||
async function confirmAddCondition() {
|
||||
const text = conditionInput.value.trim();
|
||||
if (!text) {
|
||||
function addConditionText(text: string) {
|
||||
const trimmed = String(text || "").trim();
|
||||
if (!trimmed) {
|
||||
ElMessage.warning("请输入删除条件");
|
||||
return;
|
||||
}
|
||||
addingCondition.value = true;
|
||||
try {
|
||||
const saved = await addPatrolDeleteCondition(text);
|
||||
conditionInput.value = "";
|
||||
await loadConditions();
|
||||
if (saved?.id && !selectedConditionIds.value.includes(saved.id)) {
|
||||
selectedConditionIds.value = [...selectedConditionIds.value, saved.id];
|
||||
}
|
||||
ElMessage.success("删除条件已保存");
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : "保存失败");
|
||||
} finally {
|
||||
addingCondition.value = false;
|
||||
if (taskConditionTexts.value.includes(trimmed)) {
|
||||
ElMessage.warning(`删除条件「${trimmed}」已添加`);
|
||||
return;
|
||||
}
|
||||
taskConditionTexts.value = [...taskConditionTexts.value, trimmed];
|
||||
}
|
||||
|
||||
async function removeCondition(id: number) {
|
||||
try {
|
||||
await deletePatrolDeleteCondition(id);
|
||||
selectedConditionIds.value = selectedConditionIds.value.filter(
|
||||
(item) => item !== id,
|
||||
);
|
||||
await loadConditions();
|
||||
ElMessage.success("删除条件已删除");
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : "删除失败");
|
||||
}
|
||||
/** 「添加」按钮 / 回车:把输入文本加入本次任务条件列表 */
|
||||
function addCurrentCondition() {
|
||||
addConditionText(conditionSelectValue.value);
|
||||
conditionSelectValue.value = "";
|
||||
}
|
||||
|
||||
function removeTaskConditionText(text: string) {
|
||||
taskConditionTexts.value = taskConditionTexts.value.filter((item) => item !== text);
|
||||
}
|
||||
|
||||
async function removeCandidate(id: number) {
|
||||
@@ -947,13 +915,10 @@ function buildTaskItem(item: PatrolDeleteShopQueueItem): PatrolDeleteTaskItem {
|
||||
}
|
||||
|
||||
function selectedDeleteConditions() {
|
||||
const selected = new Set(selectedConditionIds.value);
|
||||
return deleteConditions.value
|
||||
.filter((condition) => selected.has(condition.id))
|
||||
.map((condition) => ({
|
||||
id: condition.id,
|
||||
conditionText: condition.conditionText,
|
||||
}));
|
||||
return taskConditionTexts.value.map((text) => ({
|
||||
id: text,
|
||||
conditionText: text,
|
||||
}));
|
||||
}
|
||||
|
||||
function buildQueuePayload(taskId: number, items: PatrolDeleteHistoryItem[]) {
|
||||
@@ -1223,7 +1188,7 @@ onMounted(async () => {
|
||||
loadQueueState();
|
||||
loadCountryCodes();
|
||||
watch(selectedCountryCodes, saveCountryCodes, { deep: true });
|
||||
await Promise.all([loadCandidates(), loadConditions(), loadDashboard(), loadHistory()]);
|
||||
await Promise.all([loadCandidates(), loadDashboard(), loadHistory()]);
|
||||
|
||||
if (activeTaskId.value) {
|
||||
queuePushResult.value =
|
||||
@@ -1285,11 +1250,11 @@ async function batchDeleteHistory(views: TaskItemView[]) {
|
||||
.condition-input-row :deep(.el-input) { flex: 1; }
|
||||
.empty-conditions { color: #5e6878; font-size: 12px; padding: 10px 4px; }
|
||||
.condition-list { list-style: none; margin: 0; padding: 0; max-height: 150px; overflow: auto; display: flex; flex-direction: column; gap: 8px; }
|
||||
.condition-item { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 6px; border-bottom: 1px solid #333f55; }
|
||||
.condition-item:last-child { border-bottom: none; }
|
||||
.condition-check { display: flex; align-items: center; gap: 8px; min-width: 0; color: #cfd6df; font-size: 12px; cursor: pointer; }
|
||||
.condition-check input { width: 16px; height: 16px; margin: 0; flex: 0 0 auto; }
|
||||
.condition-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.condition-list--chips { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px; max-height: none; }
|
||||
.condition-chip { display: inline-flex; align-items: center; gap: 6px; padding: 4px 6px 4px 10px; border: 1px solid #3e6a8f; border-radius: 999px; background: #232a3b; color: #cfd6df; font-size: 12px; }
|
||||
.condition-chip-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 240px; }
|
||||
.condition-chip-remove { border: none; background: transparent; color: #8ba3c0; cursor: pointer; font-size: 14px; line-height: 1; padding: 0 4px; }
|
||||
.condition-chip-remove:hover { color: #f87171; }
|
||||
.link-danger { background: none; border: none; color: #e74c3c; cursor: pointer; font-size: 12px; padding: 0; }
|
||||
.link-danger:hover { text-decoration: underline; }
|
||||
.run-row { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 8px; }
|
||||
|
||||
Reference in New Issue
Block a user