|
|
@@ -14,7 +14,7 @@
|
|
|
@keyup.enter="handleSearch"
|
|
|
>
|
|
|
<template #append>
|
|
|
- <el-button icon="Search" @click="handleSearch" />
|
|
|
+ <el-button :icon="Search" @click="handleSearch" />
|
|
|
</template>
|
|
|
</el-input>
|
|
|
|
|
|
@@ -39,13 +39,24 @@
|
|
|
<el-button type="primary" plain @click="showImportDialog = true">
|
|
|
<el-icon style="margin-right: 4px"><Upload /></el-icon> Excel导入
|
|
|
</el-button>
|
|
|
+ <el-button type="warning" plain @click="handleBatchResetClick" :disabled="selectedUsers.length === 0">
|
|
|
+ <el-icon style="margin-right: 4px"><EditPen /></el-icon> 批量重置英文名
|
|
|
+ </el-button>
|
|
|
<el-button @click="openLogDrawer">
|
|
|
<el-icon style="margin-right: 4px"><List /></el-icon> 操作日志
|
|
|
</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- <el-table :data="users" v-loading="loading" stripe border style="width: 100%">
|
|
|
+ <el-table
|
|
|
+ :data="users"
|
|
|
+ v-loading="loading"
|
|
|
+ stripe
|
|
|
+ border
|
|
|
+ style="width: 100%"
|
|
|
+ @selection-change="handleSelectionChange"
|
|
|
+ >
|
|
|
+ <el-table-column type="selection" width="55" />
|
|
|
<el-table-column prop="id" label="ID" width="80" />
|
|
|
<el-table-column prop="mobile" label="手机号" min-width="120" />
|
|
|
<el-table-column prop="name" label="姓名" min-width="100" />
|
|
|
@@ -73,10 +84,13 @@
|
|
|
<el-table-column label="操作" fixed="right" width="220">
|
|
|
<template #default="scope">
|
|
|
<div class="action-buttons">
|
|
|
- <!-- Super Admin cannot be managed here usually, but if needed -->
|
|
|
+ <!-- Allow Edit for everyone, including Super Admin -->
|
|
|
+ <el-button type="primary" link @click="handleEditUser(scope.row)">编辑</el-button>
|
|
|
+
|
|
|
+ <!-- Other actions restricted for Super Admin -->
|
|
|
<template v-if="scope.row.role !== 'SUPER_ADMIN'">
|
|
|
- <el-button type="primary" link @click="handleEditUser(scope.row)">编辑</el-button>
|
|
|
<el-divider direction="vertical" />
|
|
|
+
|
|
|
<!-- PENDING Actions -->
|
|
|
<template v-if="scope.row.status === 'PENDING'">
|
|
|
<el-button type="primary" link @click="handleStatus(scope.row, 'ACTIVE')">通过</el-button>
|
|
|
@@ -115,9 +129,6 @@
|
|
|
</template>
|
|
|
</el-dropdown>
|
|
|
</template>
|
|
|
- <span v-else class="text-gray">
|
|
|
- 不可操作
|
|
|
- </span>
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
@@ -206,6 +217,36 @@
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
|
|
|
+ <!-- Batch Reset Dialog -->
|
|
|
+ <el-dialog v-model="batchResetDialogVisible" title="批量重置英文名" width="400px">
|
|
|
+ <div class="warning-text" style="margin-bottom: 20px; color: #e6a23c; display: flex; align-items: flex-start; gap: 8px;">
|
|
|
+ <el-icon style="margin-top: 2px"><Warning /></el-icon>
|
|
|
+ <span>
|
|
|
+ 将根据用户的姓名自动生成拼音英文名。如有重复将自动添加数字后缀。
|
|
|
+ <br>
|
|
|
+ 已选择 <strong>{{ selectedUsers.length }}</strong> 位用户。
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-form label-position="top">
|
|
|
+ <el-form-item label="管理员密码验证" required>
|
|
|
+ <el-input
|
|
|
+ v-model="batchAdminPassword"
|
|
|
+ type="password"
|
|
|
+ show-password
|
|
|
+ placeholder="请输入管理员密码"
|
|
|
+ :name="dynamicPwdField"
|
|
|
+ autocomplete="new-password"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="batchResetDialogVisible = false">取消</el-button>
|
|
|
+ <el-button type="primary" @click="confirmBatchReset" :loading="batchResetting">确认重置</el-button>
|
|
|
+ </template>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
<!-- Create User Dialog -->
|
|
|
<el-dialog v-model="createDialogVisible" title="新增用户" width="500px">
|
|
|
<el-form :model="createForm" :rules="createRules" ref="createFormRef" label-width="100px">
|
|
|
@@ -300,7 +341,7 @@
|
|
|
@keyup.enter="fetchLogsData"
|
|
|
>
|
|
|
<template #append>
|
|
|
- <el-button icon="Search" @click="fetchLogsData" />
|
|
|
+ <el-button :icon="Search" @click="fetchLogsData" />
|
|
|
</template>
|
|
|
</el-input>
|
|
|
<el-date-picker
|
|
|
@@ -365,7 +406,7 @@
|
|
|
<script setup lang="ts">
|
|
|
import { ref, onMounted, reactive } from 'vue'
|
|
|
import { ElMessage, FormInstance, FormRules } from 'element-plus'
|
|
|
-import { Refresh, ArrowDown, Search, Plus, List, Upload } from '@element-plus/icons-vue'
|
|
|
+import { Refresh, ArrowDown, Search, Plus, List, Upload, EditPen, Warning } from '@element-plus/icons-vue'
|
|
|
import api from '../utils/request'
|
|
|
import { getLogs, OperationLog } from '../api/logs'
|
|
|
import UserImportDialog from '../components/UserImportDialog.vue'
|
|
|
@@ -723,6 +764,50 @@ const confirmChangeRole = async () => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// Batch Reset Logic
|
|
|
+const batchResetDialogVisible = ref(false)
|
|
|
+const batchAdminPassword = ref('')
|
|
|
+const batchResetting = ref(false)
|
|
|
+const selectedUsers = ref<User[]>([])
|
|
|
+
|
|
|
+const handleSelectionChange = (val: User[]) => {
|
|
|
+ selectedUsers.value = val
|
|
|
+}
|
|
|
+
|
|
|
+const handleBatchResetClick = () => {
|
|
|
+ if (selectedUsers.value.length === 0) {
|
|
|
+ ElMessage.warning('请先选择用户')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ batchAdminPassword.value = ''
|
|
|
+ refreshDynamicField()
|
|
|
+ batchResetDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const confirmBatchReset = async () => {
|
|
|
+ if (!batchAdminPassword.value) {
|
|
|
+ ElMessage.warning('请输入管理员密码')
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ batchResetting.value = true
|
|
|
+ try {
|
|
|
+ const res = await api.post('/users/batch/reset-english-name', {
|
|
|
+ user_ids: selectedUsers.value.map(u => u.id),
|
|
|
+ admin_password: batchAdminPassword.value
|
|
|
+ })
|
|
|
+ ElMessage.success(`操作成功,已重置 ${res.data.count} 位用户的英文名`)
|
|
|
+ batchResetDialogVisible.value = false
|
|
|
+ fetchUsers()
|
|
|
+ selectedUsers.value = []
|
|
|
+ } catch (e) {
|
|
|
+ // handled
|
|
|
+ } finally {
|
|
|
+ batchResetting.value = false
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
// Log Logic
|
|
|
const logDrawerVisible = ref(false)
|
|
|
const logLoading = ref(false)
|
|
|
@@ -753,11 +838,6 @@ const fetchLogsData = async () => {
|
|
|
keyword: logFilter.keyword || undefined,
|
|
|
}
|
|
|
if (logFilter.dateRange && logFilter.dateRange.length === 2) {
|
|
|
- params.start_date = logFilter.dateRange[0]
|
|
|
- // Add time to end date to cover the whole day
|
|
|
- // Or backend handles it? Standard is often strict inequality.
|
|
|
- // Let's assume date string implies 00:00:00, so end date should be next day or use backend logic.
|
|
|
- // Simple approach: pass as is
|
|
|
params.start_date = logFilter.dateRange[0]
|
|
|
params.end_date = logFilter.dateRange[1] + ' 23:59:59'
|
|
|
}
|