|
@@ -36,6 +36,7 @@ const newsForm = reactive({
|
|
|
|
|
|
|
|
const userOptions = ref<Array<{ label: string; value: string }>>([]);
|
|
const userOptions = ref<Array<{ label: string; value: string }>>([]);
|
|
|
const loadingUsers = ref(false);
|
|
const loadingUsers = ref(false);
|
|
|
|
|
+const selectingAllUsers = ref(false);
|
|
|
const loadingEnergyList = ref(false);
|
|
const loadingEnergyList = ref(false);
|
|
|
const loadingNewsList = ref(false);
|
|
const loadingNewsList = ref(false);
|
|
|
const energyList = ref<AdminInboxListItem[]>([]);
|
|
const energyList = ref<AdminInboxListItem[]>([]);
|
|
@@ -75,6 +76,7 @@ const isLoadingBarActive = computed(
|
|
|
loadingNewsList.value ||
|
|
loadingNewsList.value ||
|
|
|
detailLoading.value,
|
|
detailLoading.value,
|
|
|
);
|
|
);
|
|
|
|
|
+const selectedUserCount = computed(() => energyForm.selectedUserIds.length);
|
|
|
|
|
|
|
|
async function loadUsers(keyword = ""): Promise<void> {
|
|
async function loadUsers(keyword = ""): Promise<void> {
|
|
|
loadingUsers.value = true;
|
|
loadingUsers.value = true;
|
|
@@ -91,6 +93,53 @@ async function loadUsers(keyword = ""): Promise<void> {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+async function selectAllUsersFromDatabase(): Promise<void> {
|
|
|
|
|
+ if (selectingAllUsers.value) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ selectingAllUsers.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const allUserIds = new Set(energyForm.selectedUserIds);
|
|
|
|
|
+ const pageSize = 100;
|
|
|
|
|
+ let page = 1;
|
|
|
|
|
+ let total = 0;
|
|
|
|
|
+ do {
|
|
|
|
|
+ const res = await fetchAdminUsers({ page, page_size: pageSize });
|
|
|
|
|
+ total = res.data.total;
|
|
|
|
|
+ for (const item of res.data.items) {
|
|
|
|
|
+ allUserIds.add(item.user_id);
|
|
|
|
|
+ }
|
|
|
|
|
+ page += 1;
|
|
|
|
|
+ } while ((page - 1) * pageSize < total);
|
|
|
|
|
+ energyForm.selectedUserIds = Array.from(allUserIds);
|
|
|
|
|
+ ElMessage.success(`已全选数据库用户,共 ${energyForm.selectedUserIds.length} 人`);
|
|
|
|
|
+ } catch (_error) {
|
|
|
|
|
+ ElMessage.error("全选用户失败,请稍后重试");
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ selectingAllUsers.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function toggleVisibleUsers(): void {
|
|
|
|
|
+ if (userOptions.value.length === 0) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const visibleUserSet = new Set(userOptions.value.map((option) => option.value));
|
|
|
|
|
+ const selectedSet = new Set(energyForm.selectedUserIds);
|
|
|
|
|
+ for (const userId of visibleUserSet) {
|
|
|
|
|
+ if (selectedSet.has(userId)) {
|
|
|
|
|
+ selectedSet.delete(userId);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ selectedSet.add(userId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ energyForm.selectedUserIds = Array.from(selectedSet);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function clearSelectedUsers(): void {
|
|
|
|
|
+ energyForm.selectedUserIds = [];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function handleMenuSelect(key: string): void {
|
|
function handleMenuSelect(key: string): void {
|
|
|
activeMenu.value = key as MenuKey;
|
|
activeMenu.value = key as MenuKey;
|
|
|
}
|
|
}
|
|
@@ -358,24 +407,41 @@ loadNewsHistory();
|
|
|
<el-input-number v-model="energyForm.energyStoneAmount" :min="1" :step="1" :precision="0" />
|
|
<el-input-number v-model="energyForm.energyStoneAmount" :min="1" :step="1" :precision="0" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item label="选择用户">
|
|
<el-form-item label="选择用户">
|
|
|
- <el-select
|
|
|
|
|
- v-model="energyForm.selectedUserIds"
|
|
|
|
|
- multiple
|
|
|
|
|
- filterable
|
|
|
|
|
- remote
|
|
|
|
|
- reserve-keyword
|
|
|
|
|
- :remote-method="loadUsers"
|
|
|
|
|
- :loading="loadingUsers"
|
|
|
|
|
- placeholder="输入手机号或邮箱检索并选择"
|
|
|
|
|
- style="width: 100%"
|
|
|
|
|
- >
|
|
|
|
|
- <el-option
|
|
|
|
|
- v-for="item in userOptions"
|
|
|
|
|
- :key="item.value"
|
|
|
|
|
- :label="item.label"
|
|
|
|
|
- :value="item.value"
|
|
|
|
|
- />
|
|
|
|
|
- </el-select>
|
|
|
|
|
|
|
+ <div style="width: 100%">
|
|
|
|
|
+ <div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px">
|
|
|
|
|
+ <span style="font-size: 13px; color: #64748b">已选 {{ selectedUserCount }} 人(当前检索 {{ userOptions.length }} 人)</span>
|
|
|
|
|
+ <div style="display: flex; gap: 8px">
|
|
|
|
|
+ <el-button
|
|
|
|
|
+ text
|
|
|
|
|
+ type="primary"
|
|
|
|
|
+ :loading="selectingAllUsers"
|
|
|
|
|
+ @click="selectAllUsersFromDatabase"
|
|
|
|
|
+ >
|
|
|
|
|
+ 全选数据库用户
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button text type="primary" @click="toggleVisibleUsers">反选当前检索</el-button>
|
|
|
|
|
+ <el-button text @click="clearSelectedUsers">清空已选</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="energyForm.selectedUserIds"
|
|
|
|
|
+ multiple
|
|
|
|
|
+ filterable
|
|
|
|
|
+ remote
|
|
|
|
|
+ reserve-keyword
|
|
|
|
|
+ :remote-method="loadUsers"
|
|
|
|
|
+ :loading="loadingUsers"
|
|
|
|
|
+ placeholder="输入手机号或邮箱检索并选择"
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in userOptions"
|
|
|
|
|
+ :key="item.value"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :value="item.value"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </div>
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
<el-form-item>
|
|
|
<el-button class="submit-btn" type="primary" :loading="submittingEnergy" @click="submitEnergyGift">
|
|
<el-button class="submit-btn" type="primary" :loading="submittingEnergy" @click="submitEnergyGift">
|