|
@@ -1,12 +1,12 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="p-8">
|
|
|
|
|
|
|
+ <div class="p-4 md:p-8">
|
|
|
<!-- 头部工具栏 -->
|
|
<!-- 头部工具栏 -->
|
|
|
<a-card class="mb-6 bg-gray-50 rounded-lg shadow-sm !border-0">
|
|
<a-card class="mb-6 bg-gray-50 rounded-lg shadow-sm !border-0">
|
|
|
- <a-space size="large">
|
|
|
|
|
|
|
+ <a-space size="large" wrap>
|
|
|
<a-input-search
|
|
<a-input-search
|
|
|
v-model:value="searchKeyword"
|
|
v-model:value="searchKeyword"
|
|
|
placeholder="搜索用户名或姓名..."
|
|
placeholder="搜索用户名或姓名..."
|
|
|
- class="w-80"
|
|
|
|
|
|
|
+ class="w-full sm:w-80"
|
|
|
@search="loadUsers"
|
|
@search="loadUsers"
|
|
|
/>
|
|
/>
|
|
|
<a-button type="primary" @click="showCreateModal" class="shadow-sm hover:shadow-md transition-shadow">
|
|
<a-button type="primary" @click="showCreateModal" class="shadow-sm hover:shadow-md transition-shadow">
|
|
@@ -16,8 +16,8 @@
|
|
|
</a-space>
|
|
</a-space>
|
|
|
</a-card>
|
|
</a-card>
|
|
|
|
|
|
|
|
- <!-- 用户列表表格 -->
|
|
|
|
|
- <a-card class="rounded-xl shadow-md !border-0">
|
|
|
|
|
|
|
+ <!-- 桌面端:用户列表表格 -->
|
|
|
|
|
+ <a-card v-if="!layoutStore.isMobile" class="rounded-xl shadow-md !border-0">
|
|
|
<a-table
|
|
<a-table
|
|
|
:columns="columns"
|
|
:columns="columns"
|
|
|
:dataSource="users"
|
|
:dataSource="users"
|
|
@@ -25,6 +25,7 @@
|
|
|
:pagination="pagination"
|
|
:pagination="pagination"
|
|
|
@change="handleTableChange"
|
|
@change="handleTableChange"
|
|
|
rowKey="id"
|
|
rowKey="id"
|
|
|
|
|
+ :scroll="{ x: 1130 }"
|
|
|
>
|
|
>
|
|
|
<!-- 状态列 -->
|
|
<!-- 状态列 -->
|
|
|
<template #status="{ record }">
|
|
<template #status="{ record }">
|
|
@@ -67,6 +68,67 @@
|
|
|
</a-table>
|
|
</a-table>
|
|
|
</a-card>
|
|
</a-card>
|
|
|
|
|
|
|
|
|
|
+ <!-- 移动端:用户卡片列表 -->
|
|
|
|
|
+ <div v-else class="space-y-3">
|
|
|
|
|
+ <a-spin :spinning="loading">
|
|
|
|
|
+ <a-empty v-if="!loading && users.length === 0" description="暂无用户数据" class="py-8" />
|
|
|
|
|
+ <a-card
|
|
|
|
|
+ v-for="item in users"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ class="rounded-xl shadow-sm !border-0"
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="flex items-start justify-between gap-2 mb-2">
|
|
|
|
|
+ <span class="font-medium text-gray-900">{{ item.name }}</span>
|
|
|
|
|
+ <div class="flex gap-1 flex-shrink-0">
|
|
|
|
|
+ <a-tag :color="item.status === 1 ? 'green' : 'red'" class="!rounded-full !mr-0">
|
|
|
|
|
+ {{ item.status === 1 ? '正常' : '禁用' }}
|
|
|
|
|
+ </a-tag>
|
|
|
|
|
+ <a-tag :color="item.must_change_password ? 'orange' : 'blue'" class="!rounded-full !mr-0">
|
|
|
|
|
+ {{ item.must_change_password ? '需改密' : '已改密' }}
|
|
|
|
|
+ </a-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="text-sm text-gray-500 space-y-1 mb-3">
|
|
|
|
|
+ <div>用户名:{{ item.username }}</div>
|
|
|
|
|
+ <div>手机号:{{ item.phone }}</div>
|
|
|
|
|
+ <div>ID:{{ item.id }} · 创建:{{ item.created_at }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="flex items-center gap-2 pt-3 border-t border-gray-100">
|
|
|
|
|
+ <a-button size="small" @click="showEditModal(item)">
|
|
|
|
|
+ <EditOutlined class="mr-1" />
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </a-button>
|
|
|
|
|
+ <a-button size="small" @click="showResetPasswordModal(item)">
|
|
|
|
|
+ <KeyOutlined class="mr-1" />
|
|
|
|
|
+ 重置密码
|
|
|
|
|
+ </a-button>
|
|
|
|
|
+ <a-popconfirm
|
|
|
|
|
+ title="确定删除此用户吗?"
|
|
|
|
|
+ ok-text="确定"
|
|
|
|
|
+ cancel-text="取消"
|
|
|
|
|
+ @confirm="deleteUser(item.id)"
|
|
|
|
|
+ >
|
|
|
|
|
+ <a-button size="small" danger>
|
|
|
|
|
+ <DeleteOutlined class="mr-1" />
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </a-button>
|
|
|
|
|
+ </a-popconfirm>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </a-card>
|
|
|
|
|
+ </a-spin>
|
|
|
|
|
+ <div class="flex justify-center mt-4">
|
|
|
|
|
+ <a-pagination
|
|
|
|
|
+ v-model:current="pagination.current"
|
|
|
|
|
+ v-model:pageSize="pagination.pageSize"
|
|
|
|
|
+ :total="pagination.total"
|
|
|
|
|
+ simple
|
|
|
|
|
+ size="small"
|
|
|
|
|
+ @change="handlePageChange"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<!-- 创建/编辑用户模态框 -->
|
|
<!-- 创建/编辑用户模态框 -->
|
|
|
<a-modal
|
|
<a-modal
|
|
|
v-model:open="modalVisible"
|
|
v-model:open="modalVisible"
|
|
@@ -74,7 +136,7 @@
|
|
|
@ok="handleSubmit"
|
|
@ok="handleSubmit"
|
|
|
@cancel="resetForm"
|
|
@cancel="resetForm"
|
|
|
:confirmLoading="submitting"
|
|
:confirmLoading="submitting"
|
|
|
- width="600px"
|
|
|
|
|
|
|
+ :width="modalWidth"
|
|
|
class="!rounded-xl"
|
|
class="!rounded-xl"
|
|
|
>
|
|
>
|
|
|
<a-form
|
|
<a-form
|
|
@@ -134,6 +196,7 @@
|
|
|
@ok="handleResetPassword"
|
|
@ok="handleResetPassword"
|
|
|
@cancel="resetPasswordForm"
|
|
@cancel="resetPasswordForm"
|
|
|
:confirmLoading="resettingPassword"
|
|
:confirmLoading="resettingPassword"
|
|
|
|
|
+ :width="modalWidth"
|
|
|
class="!rounded-xl"
|
|
class="!rounded-xl"
|
|
|
>
|
|
>
|
|
|
<a-form
|
|
<a-form
|
|
@@ -158,12 +221,13 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-import { ref, reactive, onMounted } from 'vue';
|
|
|
|
|
|
|
+import { ref, reactive, onMounted, computed } from 'vue';
|
|
|
import { message } from 'ant-design-vue';
|
|
import { message } from 'ant-design-vue';
|
|
|
import { PlusOutlined, EditOutlined, KeyOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
|
import { PlusOutlined, EditOutlined, KeyOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
|
|
import { UserApi, createApiClient } from '@smartcut/api-client';
|
|
import { UserApi, createApiClient } from '@smartcut/api-client';
|
|
|
import { STORAGE_KEYS, validatePasswordStrength } from '@smartcut/shared-utils';
|
|
import { STORAGE_KEYS, validatePasswordStrength } from '@smartcut/shared-utils';
|
|
|
import { getApiBaseUrl } from '@/apiConfig';
|
|
import { getApiBaseUrl } from '@/apiConfig';
|
|
|
|
|
+import { useLayoutStore } from '@/stores/layout';
|
|
|
import type { SystemUser } from '@smartcut/types';
|
|
import type { SystemUser } from '@smartcut/types';
|
|
|
|
|
|
|
|
// API客户端
|
|
// API客户端
|
|
@@ -174,6 +238,9 @@ const apiClient = createApiClient({
|
|
|
|
|
|
|
|
const userApi = new UserApi(apiClient);
|
|
const userApi = new UserApi(apiClient);
|
|
|
|
|
|
|
|
|
|
+const layoutStore = useLayoutStore();
|
|
|
|
|
+const modalWidth = computed(() => layoutStore.isMobile ? '90%' : 600);
|
|
|
|
|
+
|
|
|
// 用户列表数据
|
|
// 用户列表数据
|
|
|
const users = ref<SystemUser[]>([]);
|
|
const users = ref<SystemUser[]>([]);
|
|
|
const loading = ref(false);
|
|
const loading = ref(false);
|
|
@@ -334,6 +401,13 @@ function handleTableChange(pag: any) {
|
|
|
loadUsers();
|
|
loadUsers();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// 移动端卡片列表分页变化
|
|
|
|
|
+function handlePageChange(page: number, pageSize: number) {
|
|
|
|
|
+ pagination.current = page;
|
|
|
|
|
+ pagination.pageSize = pageSize;
|
|
|
|
|
+ loadUsers();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// 显示创建模态框
|
|
// 显示创建模态框
|
|
|
function showCreateModal() {
|
|
function showCreateModal() {
|
|
|
modalTitle.value = '创建用户';
|
|
modalTitle.value = '创建用户';
|