-import { computed, onMounted, onUnmounted, reactive, ref, watch } from "vue";
+import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from "vue";
import { ElMessage } from "element-plus";
import Edit from "./components/edit.vue";
import DetailDialog from "./components/detail.vue";
@@ -40,6 +40,7 @@ const extractForm = reactive({
platform: "local",
type: "account",
remark: "",
+ replenish: false,
});
const tableData = ref([]);
@@ -54,6 +55,9 @@ const pagination = reactive({
pageSize: 20,
});
+/** 跳转未提取末页时跳过 watcher,避免先被重置到第 1 页 */
+const skipWatchFetchDuringUnusedJump = ref(false);
+
const pagedList = computed(() => tableData.value);
function resetQuery() {
@@ -73,6 +77,7 @@ const typeTabs = computed(() => {
watch(
() => [query.keyword, query.status, activeTypeTab.value],
() => {
+ if (skipWatchFetchDuringUnusedJump.value) return;
pagination.page = 1;
fetchList();
},
@@ -81,6 +86,7 @@ watch(
watch(
() => [pagination.page, pagination.pageSize],
() => {
+ if (skipWatchFetchDuringUnusedJump.value) return;
fetchList();
},
);
@@ -142,6 +148,7 @@ function openExtractDialog() {
extractTargetRow.value = null;
extractForm.platform = "local";
extractForm.type = "account";
+ extractForm.replenish = false;
extractVisible.value = true;
}
@@ -150,6 +157,7 @@ function openExtractByRow(row) {
extractForm.platform = "local";
extractForm.type = row.type;
extractForm.remark = row.remark || "";
+ extractForm.replenish = false;
extractVisible.value = true;
}
@@ -165,6 +173,14 @@ function buildCopyTextByRow(row) {
return parts.join('\n');
}
+function rowToText(row) {
+ const parts = [];
+ if (row?.account) parts.push(row.account);
+ if (row?.password) parts.push(row.password);
+ if (row?.token) parts.push(row.token);
+ return parts.join(' / ');
+}
+
async function copyToClipboard(text) {
if (!text) {
ElMessage.warning('无可复制内容');
@@ -193,6 +209,7 @@ async function handleExtract() {
type: target.type,
platform: extractForm.platform,
remark: extractForm.remark || "",
+ replenish: !!extractForm.replenish,
});
if (res?.code !== 200) {
ElMessage.error(res?.msg || "提取失败");
@@ -270,12 +287,46 @@ async function handleBatchExtract() {
}
}
+async function handleReplenish() {
+ loading.value = true;
+ try {
+ const res = await replenishAccountPool(moduleKey, {
+ type: replenishForm.type,
+ platform: replenishForm.platform,
+ remark: replenishForm.remark || "",
+ });
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || "补号失败");
+ return;
+ }
+ ElMessage.success("补号成功,已复制到剪贴板");
+ replenishVisible.value = false;
+ const row = normalizeRow(res.data || {});
+ navigator.clipboard.writeText(rowToText(row)).catch(() => {});
+ await fetchList();
+ } finally {
+ loading.value = false;
+ }
+}
+
function typeText(type) {
if (type === "account") return "账号密码";
if (type === "account_tk") return "账号密码+Token";
return "Token";
}
+function extractStatusLabel(row) {
+ if (row?.extractStatus === 2) return "补号";
+ if (row?.extracted) return "已提取";
+ return "未提取";
+}
+
+function extractStatusTagType(row) {
+ if (row?.extractStatus === 2) return "warning";
+ if (row?.extracted) return "success";
+ return "info";
+}
+
const tooltipOpts = {
popperClass: 'pool-tooltip',
popperStyle: { maxWidth: '600px', wordBreak: 'break-all', whiteSpace: 'pre-wrap' },
@@ -318,6 +369,8 @@ function normalizeRow(raw) {
const p = (v) => String(v).padStart(2, "0");
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
};
+ const st = Number(pick("is_extracted", "isExtracted", "IsExtracted"));
+ const extractStatus = Number.isFinite(st) ? st : 0;
return {
id: pick("id", "Id", "ID"),
type: pick("data_type", "dataType", "type"),
@@ -325,7 +378,8 @@ function normalizeRow(raw) {
password: pick("password", "Password"),
token: pick("token", "Token"),
remark: pick("remark", "Remark"),
- extracted: Number(pick("is_extracted", "isExtracted", "IsExtracted")) === 1,
+ extractStatus,
+ extracted: extractStatus !== 0,
extractedAt: formatTime(pickNullable("extracted_time", "extractedAt")),
extractedPlatform: pickNullable("extracted_platform", "extractedPlatform"),
createdAt: formatTime(pick("create_time", "createdAt")),
@@ -354,6 +408,35 @@ async function fetchList() {
}
}
+/** 筛选「未提取」并翻到该条件下的最后一页(当前关键词、账号类型 tab 不变) */
+async function jumpToLastUnusedPage() {
+ const type = activeTypeTab.value === "all" ? undefined : activeTypeTab.value;
+ const res = await getAccountPoolList(moduleKey, {
+ page: 1,
+ pageSize: pagination.pageSize,
+ keyword: query.keyword || undefined,
+ status: "unused",
+ type,
+ });
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || "获取列表失败");
+ return;
+ }
+ const cnt = Number(res?.data?.total || 0);
+ if (cnt === 0) {
+ ElMessage.warning("暂无未提取数据");
+ return;
+ }
+ const lastPage = Math.max(1, Math.ceil(cnt / pagination.pageSize));
+ skipWatchFetchDuringUnusedJump.value = true;
+ pagination.page = lastPage;
+ query.status = "unused";
+ await nextTick();
+ skipWatchFetchDuringUnusedJump.value = false;
+ await fetchList();
+ ElMessage.success(`已跳转未提取第 ${lastPage} 页(共 ${cnt} 条)`);
+}
+
function updateDeviceType() {
isMobile.value = window.innerWidth <= 768;
}
@@ -470,6 +553,14 @@ function copyCardInfo(row) {
+
+ 未提取末页
+
重置
@@ -517,8 +608,8 @@ function copyCardInfo(row) {
-
- {{ row.extracted ? "已提取" : "未提取" }}
+
+ {{ extractStatusLabel(row) }}
@@ -541,9 +632,9 @@ function copyCardInfo(row) {
>详情
提取
@@ -586,9 +677,11 @@ function copyCardInfo(row) {
:type="extractForm.type"
:platform="extractForm.platform"
:remark="extractForm.remark"
+ :replenish="extractForm.replenish"
:platform-map="PLATFORM_MAP"
@update:platform="(v) => (extractForm.platform = v)"
@update:remark="(v) => (extractForm.remark = v)"
+ @update:replenish="(v) => (extractForm.replenish = v)"
@confirm="handleExtract"
/>
diff --git a/src/views/accountpool/kiro/components/detail.vue b/src/views/accountpool/kiro/components/detail.vue
index d2ad3fe..1874d14 100644
--- a/src/views/accountpool/kiro/components/detail.vue
+++ b/src/views/accountpool/kiro/components/detail.vue
@@ -90,7 +90,9 @@ function copyToken() {
{{ row.token || '-' }}
- {{ row.extracted ? '已提取' : '未提取' }}
+ {{
+ row.extractStatus === 2 ? '补号' : row.extracted ? '已提取' : '未提取'
+ }}
{{ row.extractedAt || '-' }}
{{ platformLabel }}
diff --git a/src/views/accountpool/kiro/components/extract.vue b/src/views/accountpool/kiro/components/extract.vue
index ac9b7fe..476efb2 100644
--- a/src/views/accountpool/kiro/components/extract.vue
+++ b/src/views/accountpool/kiro/components/extract.vue
@@ -5,10 +5,17 @@ const props = defineProps({
type: { type: String, default: 'account' },
platform: { type: String, default: 'local' },
remark: { type: String, default: '' },
+ replenish: { type: Boolean, default: false },
platformMap: { type: Object, default: () => ({}) },
});
-const emit = defineEmits(['update:modelValue', 'update:platform', 'update:remark', 'confirm']);
+const emit = defineEmits([
+ 'update:modelValue',
+ 'update:platform',
+ 'update:remark',
+ 'update:replenish',
+ 'confirm',
+]);
function typeText(type) {
if (type === 'account') return '账号密码';
@@ -29,6 +36,14 @@ function typeText(type) {
+
+ emit('update:replenish', v)"
+ />
+
-import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
+import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import Edit from './components/edit.vue';
import DetailDialog from './components/detail.vue';
@@ -34,7 +34,7 @@ const patchVisible = ref(false);
const query = reactive({ keyword: "", status: "" });
const activeTypeTab = ref("all");
-const extractForm = reactive({ platform: 'local', type: 'account', remark: '' });
+const extractForm = reactive({ platform: 'local', type: 'account', remark: '', replenish: false });
const tableData = ref([]);
const total = ref(0);
@@ -44,6 +44,8 @@ const detailRemarkSaving = ref(false);
const isMobile = ref(false);
const pagination = reactive({ page: 1, pageSize: 30 });
+const skipWatchFetchDuringUnusedJump = ref(false);
+
const pagedList = computed(() => tableData.value);
function resetQuery() {
@@ -61,6 +63,7 @@ const typeTabs = computed(() => [
watch(
() => [query.keyword, query.status, activeTypeTab.value],
() => {
+ if (skipWatchFetchDuringUnusedJump.value) return;
pagination.page = 1;
fetchList();
},
@@ -68,6 +71,7 @@ watch(
watch(
() => [pagination.page, pagination.pageSize],
() => {
+ if (skipWatchFetchDuringUnusedJump.value) return;
fetchList();
},
);
@@ -124,6 +128,7 @@ function openExtractByRow(row) {
extractForm.platform = "local";
extractForm.type = row.type;
extractForm.remark = row.remark || '';
+ extractForm.replenish = false;
extractVisible.value = true;
}
@@ -157,7 +162,11 @@ async function handleExtract() {
const target = extractTargetRow.value;
if (!target) { ElMessage.warning("未找到提取目标"); return; }
const res = await extractAccountPool(moduleKey, {
- id: target.id, type: target.type, platform: extractForm.platform, remark: extractForm.remark || '',
+ id: target.id,
+ type: target.type,
+ platform: extractForm.platform,
+ remark: extractForm.remark || '',
+ replenish: !!extractForm.replenish,
});
if (res?.code !== 200) { ElMessage.error(res?.msg || "提取失败"); return; }
ElMessage.success("提取成功");
@@ -294,6 +303,8 @@ function normalizeRow(raw) {
const p = (v) => String(v).padStart(2, "0");
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
};
+ const st = Number(pick("is_extracted", "isExtracted", "IsExtracted"));
+ const extractStatus = Number.isFinite(st) ? st : 0;
return {
id: pick("id", "Id", "ID"),
type: pick("data_type", "dataType", "type"),
@@ -301,13 +312,26 @@ function normalizeRow(raw) {
password: pick("password", "Password"),
token: pick("token", "Token"),
remark: pick("remark", "Remark"),
- extracted: Number(pick("is_extracted", "isExtracted", "IsExtracted")) === 1,
+ extractStatus,
+ extracted: extractStatus !== 0,
extractedAt: formatTime(pickNullable("extracted_time", "extractedAt")),
extractedPlatform: pickNullable("extracted_platform", "extractedPlatform"),
createdAt: formatTime(pick("create_time", "createdAt")),
};
}
+function extractStatusLabel(row) {
+ if (row?.extractStatus === 2) return "补号";
+ if (row?.extracted) return "已提取";
+ return "未提取";
+}
+
+function extractStatusTagType(row) {
+ if (row?.extractStatus === 2) return "warning";
+ if (row?.extracted) return "success";
+ return "info";
+}
+
async function fetchList() {
loading.value = true;
try {
@@ -330,6 +354,34 @@ async function fetchList() {
}
}
+async function jumpToLastUnusedPage() {
+ const type = activeTypeTab.value === "all" ? undefined : activeTypeTab.value;
+ const res = await getAccountPoolList(moduleKey, {
+ page: 1,
+ pageSize: pagination.pageSize,
+ keyword: query.keyword || undefined,
+ status: "unused",
+ type,
+ });
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || "获取列表失败");
+ return;
+ }
+ const cnt = Number(res?.data?.total || 0);
+ if (cnt === 0) {
+ ElMessage.warning("暂无未提取数据");
+ return;
+ }
+ const lastPage = Math.max(1, Math.ceil(cnt / pagination.pageSize));
+ skipWatchFetchDuringUnusedJump.value = true;
+ pagination.page = lastPage;
+ query.status = "unused";
+ await nextTick();
+ skipWatchFetchDuringUnusedJump.value = false;
+ await fetchList();
+ ElMessage.success(`已跳转未提取第 ${lastPage} 页(共 ${cnt} 条)`);
+}
+
onMounted(() => { fetchList(); });
// ---- 接口说明数据 ----
@@ -428,6 +480,14 @@ function copyCardInfo(row) {
+
+ 未提取末页
+
重置
@@ -465,8 +525,8 @@ function copyCardInfo(row) {
- {{
- row.extracted ? "已提取" : "未提取"
+ {{
+ extractStatusLabel(row)
}}
@@ -489,9 +549,9 @@ function copyCardInfo(row) {
>详情
提取
@@ -534,9 +594,11 @@ function copyCardInfo(row) {
:type="extractForm.type"
:platform="extractForm.platform"
:remark="extractForm.remark"
+ :replenish="extractForm.replenish"
:platform-map="PLATFORM_MAP"
@update:platform="(v) => (extractForm.platform = v)"
@update:remark="(v) => (extractForm.remark = v)"
+ @update:replenish="(v) => (extractForm.replenish = v)"
@confirm="handleExtract"
/>
diff --git a/src/views/accountpool/windsurf/components/detail.vue b/src/views/accountpool/windsurf/components/detail.vue
index d2ad3fe..1874d14 100644
--- a/src/views/accountpool/windsurf/components/detail.vue
+++ b/src/views/accountpool/windsurf/components/detail.vue
@@ -90,7 +90,9 @@ function copyToken() {
{{ row.token || '-' }}
- {{ row.extracted ? '已提取' : '未提取' }}
+ {{
+ row.extractStatus === 2 ? '补号' : row.extracted ? '已提取' : '未提取'
+ }}
{{ row.extractedAt || '-' }}
{{ platformLabel }}
diff --git a/src/views/accountpool/windsurf/components/extract.vue b/src/views/accountpool/windsurf/components/extract.vue
index ac9b7fe..476efb2 100644
--- a/src/views/accountpool/windsurf/components/extract.vue
+++ b/src/views/accountpool/windsurf/components/extract.vue
@@ -5,10 +5,17 @@ const props = defineProps({
type: { type: String, default: 'account' },
platform: { type: String, default: 'local' },
remark: { type: String, default: '' },
+ replenish: { type: Boolean, default: false },
platformMap: { type: Object, default: () => ({}) },
});
-const emit = defineEmits(['update:modelValue', 'update:platform', 'update:remark', 'confirm']);
+const emit = defineEmits([
+ 'update:modelValue',
+ 'update:platform',
+ 'update:remark',
+ 'update:replenish',
+ 'confirm',
+]);
function typeText(type) {
if (type === 'account') return '账号密码';
@@ -29,6 +36,14 @@ function typeText(type) {
+
+ emit('update:replenish', v)"
+ />
+
-import { computed, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
+import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watch } from 'vue';
import { ElMessage } from 'element-plus';
import Edit from './components/edit.vue';
import DetailDialog from './components/detail.vue';
@@ -33,7 +33,7 @@ const patchVisible = ref(false);
const query = reactive({ keyword: "", status: "" });
const activeTypeTab = ref("all");
-const extractForm = reactive({ platform: 'local', type: 'account', remark: '' });
+const extractForm = reactive({ platform: 'local', type: 'account', remark: '', replenish: false });
const tableData = ref([]);
const total = ref(0);
@@ -43,6 +43,8 @@ const detailRemarkSaving = ref(false);
const isMobile = ref(false);
const pagination = reactive({ page: 1, pageSize: 30 });
+const skipWatchFetchDuringUnusedJump = ref(false);
+
const pagedList = computed(() => tableData.value);
function resetQuery() {
@@ -60,6 +62,7 @@ const typeTabs = computed(() => [
watch(
() => [query.keyword, query.status, activeTypeTab.value],
() => {
+ if (skipWatchFetchDuringUnusedJump.value) return;
pagination.page = 1;
fetchList();
},
@@ -67,6 +70,7 @@ watch(
watch(
() => [pagination.page, pagination.pageSize],
() => {
+ if (skipWatchFetchDuringUnusedJump.value) return;
fetchList();
},
);
@@ -123,6 +127,7 @@ function openExtractByRow(row) {
extractForm.platform = "local";
extractForm.type = row.type;
extractForm.remark = row.remark || '';
+ extractForm.replenish = false;
extractVisible.value = true;
}
@@ -156,7 +161,11 @@ async function handleExtract() {
const target = extractTargetRow.value;
if (!target) { ElMessage.warning("未找到提取目标"); return; }
const res = await extractAccountPool(moduleKey, {
- id: target.id, type: target.type, platform: extractForm.platform, remark: extractForm.remark || '',
+ id: target.id,
+ type: target.type,
+ platform: extractForm.platform,
+ remark: extractForm.remark || '',
+ replenish: !!extractForm.replenish,
});
if (res?.code !== 200) { ElMessage.error(res?.msg || "提取失败"); return; }
ElMessage.success("提取成功");
@@ -293,6 +302,8 @@ function normalizeRow(raw) {
const p = (v) => String(v).padStart(2, "0");
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
};
+ const st = Number(pick("is_extracted", "isExtracted", "IsExtracted"));
+ const extractStatus = Number.isFinite(st) ? st : 0;
return {
id: pick("id", "Id", "ID"),
type: pick("data_type", "dataType", "type"),
@@ -300,13 +311,26 @@ function normalizeRow(raw) {
password: pick("password", "Password"),
token: pick("token", "Token"),
remark: pick("remark", "Remark"),
- extracted: Number(pick("is_extracted", "isExtracted", "IsExtracted")) === 1,
+ extractStatus,
+ extracted: extractStatus !== 0,
extractedAt: formatTime(pickNullable("extracted_time", "extractedAt")),
extractedPlatform: pickNullable("extracted_platform", "extractedPlatform"),
createdAt: formatTime(pick("create_time", "createdAt")),
};
}
+function extractStatusLabel(row) {
+ if (row?.extractStatus === 2) return "补号";
+ if (row?.extracted) return "已提取";
+ return "未提取";
+}
+
+function extractStatusTagType(row) {
+ if (row?.extractStatus === 2) return "warning";
+ if (row?.extracted) return "success";
+ return "info";
+}
+
async function fetchList() {
loading.value = true;
try {
@@ -329,6 +353,34 @@ async function fetchList() {
}
}
+async function jumpToLastUnusedPage() {
+ const type = activeTypeTab.value === 'all' ? undefined : activeTypeTab.value;
+ const res = await getAccountPoolList(moduleKey, {
+ page: 1,
+ pageSize: pagination.pageSize,
+ keyword: query.keyword || undefined,
+ status: 'unused',
+ type,
+ });
+ if (res?.code !== 200) {
+ ElMessage.error(res?.msg || '获取列表失败');
+ return;
+ }
+ const cnt = Number(res?.data?.total || 0);
+ if (cnt === 0) {
+ ElMessage.warning('暂无未提取数据');
+ return;
+ }
+ const lastPage = Math.max(1, Math.ceil(cnt / pagination.pageSize));
+ skipWatchFetchDuringUnusedJump.value = true;
+ pagination.page = lastPage;
+ query.status = 'unused';
+ await nextTick();
+ skipWatchFetchDuringUnusedJump.value = false;
+ await fetchList();
+ ElMessage.success(`已跳转未提取第 ${lastPage} 页(共 ${cnt} 条)`);
+}
+
onMounted(() => { fetchList(); });
// ---- 接口说明数据 ----
@@ -427,6 +479,14 @@ function copyCardInfo(row) {
+
+ 未提取末页
+
重置