commits
This commit is contained in:
@@ -1,244 +1,244 @@
|
||||
<template>
|
||||
<div class="tenant-users-tab">
|
||||
<div class="section-header">
|
||||
<div class="section-title">用户列表</div>
|
||||
<el-button type="primary" size="small" :disabled="!tid" @click="handleAddUser">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加用户
|
||||
</el-button>
|
||||
</div>
|
||||
<el-form :inline="true" class="user-search-form" @submit.prevent>
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="userSearchKeyword"
|
||||
clearable
|
||||
placeholder="姓名 / 手机 / 邮箱 / 账号"
|
||||
style="width: 260px"
|
||||
:disabled="!tid"
|
||||
@keyup.enter="handleSearchUsers"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="!tid" @click="handleSearchUsers">查询</el-button>
|
||||
<el-button :disabled="!tid" @click="resetUserSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="tenantUsers" v-loading="usersLoading" border style="width: 100%">
|
||||
<el-table-column prop="account" label="用户名" min-width="140" align="center" />
|
||||
<el-table-column prop="name" label="姓名" min-width="120" align="center" />
|
||||
<el-table-column prop="phone" label="手机号" min-width="140" align="center" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" align="center" />
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="Number(row.status) === 1 ? 'success' : 'danger'">
|
||||
{{ Number(row.status) === 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" @click="openPasswordDialog(row)">
|
||||
修改密码
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AddUser ref="addUserRef" @success="refreshTenantUsers" />
|
||||
|
||||
<el-dialog v-model="passwordDialogVisible" title="修改密码" width="420px" destroy-on-close>
|
||||
<el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordRules" label-width="90px">
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="passwordForm.password" type="password" show-password placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="password2">
|
||||
<el-input v-model="passwordForm.password2" type="password" show-password placeholder="请再次输入新密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="passwordDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="passwordSubmitting" @click="submitPassword">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import { getTenantUserList, editTenantUser } from "@/api/tenantUser";
|
||||
import AddUser from "./adduser.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
/** 当前租户 ID,为空时不请求 */
|
||||
tid: number | null;
|
||||
}>();
|
||||
|
||||
const tenantUsers = ref<any[]>([]);
|
||||
const usersLoading = ref(false);
|
||||
const userSearchKeyword = ref("");
|
||||
const addUserRef = ref<{ open: (tenantId: number) => void } | null>(null);
|
||||
|
||||
const passwordDialogVisible = ref(false);
|
||||
const passwordSubmitting = ref(false);
|
||||
const currentTenantUserId = ref<number | null>(null);
|
||||
const passwordFormRef = ref();
|
||||
const passwordForm = reactive({
|
||||
password: "",
|
||||
password2: "",
|
||||
});
|
||||
|
||||
const passwordRules = {
|
||||
password: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 5, message: "密码至少 5 位", trigger: "blur" },
|
||||
],
|
||||
password2: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
if (!value) return callback(new Error("请再次输入新密码"));
|
||||
if (value !== passwordForm.password) return callback(new Error("两次密码不一致"));
|
||||
callback();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const buildTenantUserQuery = (tid: number) => {
|
||||
const params: Record<string, string | number> = { tid };
|
||||
const kw = userSearchKeyword.value.trim();
|
||||
if (kw) params.keyword = kw;
|
||||
return params;
|
||||
};
|
||||
|
||||
const refreshTenantUsers = async () => {
|
||||
const id = props.tid;
|
||||
if (id == null) return;
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getTenantUserList(buildTenantUserQuery(id));
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch {
|
||||
tenantUsers.value = [];
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadUsersForTid = async (tid: number) => {
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getTenantUserList({ tid });
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch {
|
||||
tenantUsers.value = [];
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.tid,
|
||||
(id) => {
|
||||
userSearchKeyword.value = "";
|
||||
if (id == null) {
|
||||
tenantUsers.value = [];
|
||||
return;
|
||||
}
|
||||
loadUsersForTid(id);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleSearchUsers = () => {
|
||||
refreshTenantUsers();
|
||||
};
|
||||
|
||||
const resetUserSearch = () => {
|
||||
userSearchKeyword.value = "";
|
||||
refreshTenantUsers();
|
||||
};
|
||||
|
||||
const handleAddUser = () => {
|
||||
if (props.tid != null) {
|
||||
addUserRef.value?.open(props.tid);
|
||||
}
|
||||
};
|
||||
|
||||
const openPasswordDialog = (row: any) => {
|
||||
currentTenantUserId.value = Number(row?.id || 0) || null;
|
||||
passwordForm.password = "";
|
||||
passwordForm.password2 = "";
|
||||
passwordDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const submitPassword = async () => {
|
||||
if (!passwordFormRef.value || !currentTenantUserId.value) return;
|
||||
|
||||
try {
|
||||
await passwordFormRef.value.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
passwordSubmitting.value = true;
|
||||
try {
|
||||
const res = await editTenantUser(currentTenantUserId.value, { password: passwordForm.password });
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
passwordDialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error(res.msg || "密码修改失败");
|
||||
}
|
||||
} finally {
|
||||
passwordSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
refreshTenantUsers,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tenant-users-tab {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.user-search-form {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.user-search-form :deep(.el-form-item) {
|
||||
margin-bottom: 8px;
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div class="tenant-users-tab">
|
||||
<div class="section-header">
|
||||
<div class="section-title">用户列表</div>
|
||||
<el-button type="primary" size="small" :disabled="!tid" @click="handleAddUser">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加用户
|
||||
</el-button>
|
||||
</div>
|
||||
<el-form :inline="true" class="user-search-form" @submit.prevent>
|
||||
<el-form-item label="关键词">
|
||||
<el-input
|
||||
v-model="userSearchKeyword"
|
||||
clearable
|
||||
placeholder="姓名 / 手机 / 邮箱 / 账号"
|
||||
style="width: 260px"
|
||||
:disabled="!tid"
|
||||
@keyup.enter="handleSearchUsers"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" :disabled="!tid" @click="handleSearchUsers">查询</el-button>
|
||||
<el-button :disabled="!tid" @click="resetUserSearch">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="tenantUsers" v-loading="usersLoading" border style="width: 100%">
|
||||
<el-table-column prop="account" label="用户名" min-width="140" align="center" />
|
||||
<el-table-column prop="name" label="姓名" min-width="120" align="center" />
|
||||
<el-table-column prop="phone" label="手机号" min-width="140" align="center" />
|
||||
<el-table-column prop="email" label="邮箱" min-width="180" align="center" />
|
||||
<el-table-column prop="status" label="状态" width="90" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="Number(row.status) === 1 ? 'success' : 'danger'">
|
||||
{{ Number(row.status) === 1 ? "启用" : "禁用" }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="140" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button text type="primary" @click="openPasswordDialog(row)">
|
||||
修改密码
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<AddUser ref="addUserRef" @success="refreshTenantUsers" />
|
||||
|
||||
<el-dialog v-model="passwordDialogVisible" title="修改密码" width="420px" destroy-on-close>
|
||||
<el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordRules" label-width="90px">
|
||||
<el-form-item label="新密码" prop="password">
|
||||
<el-input v-model="passwordForm.password" type="password" show-password placeholder="请输入新密码" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="password2">
|
||||
<el-input v-model="passwordForm.password2" type="password" show-password placeholder="请再次输入新密码" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="passwordDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="passwordSubmitting" @click="submitPassword">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { Plus } from "@element-plus/icons-vue";
|
||||
import { getTenantUserList, editTenantUser } from "@/api/tenantUser";
|
||||
import AddUser from "./adduser.vue";
|
||||
|
||||
const props = defineProps<{
|
||||
/** 当前租户 ID,为空时不请求 */
|
||||
tid: number | null;
|
||||
}>();
|
||||
|
||||
const tenantUsers = ref<any[]>([]);
|
||||
const usersLoading = ref(false);
|
||||
const userSearchKeyword = ref("");
|
||||
const addUserRef = ref<{ open: (tenantId: number) => void } | null>(null);
|
||||
|
||||
const passwordDialogVisible = ref(false);
|
||||
const passwordSubmitting = ref(false);
|
||||
const currentTenantUserId = ref<number | null>(null);
|
||||
const passwordFormRef = ref();
|
||||
const passwordForm = reactive({
|
||||
password: "",
|
||||
password2: "",
|
||||
});
|
||||
|
||||
const passwordRules = {
|
||||
password: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 5, message: "密码至少 5 位", trigger: "blur" },
|
||||
],
|
||||
password2: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
validator: (_rule: any, value: string, callback: any) => {
|
||||
if (!value) return callback(new Error("请再次输入新密码"));
|
||||
if (value !== passwordForm.password) return callback(new Error("两次密码不一致"));
|
||||
callback();
|
||||
},
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const buildTenantUserQuery = (tid: number) => {
|
||||
const params: Record<string, string | number> = { tid };
|
||||
const kw = userSearchKeyword.value.trim();
|
||||
if (kw) params.keyword = kw;
|
||||
return params;
|
||||
};
|
||||
|
||||
const refreshTenantUsers = async () => {
|
||||
const id = props.tid;
|
||||
if (id == null) return;
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getTenantUserList(buildTenantUserQuery(id));
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch {
|
||||
tenantUsers.value = [];
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const loadUsersForTid = async (tid: number) => {
|
||||
usersLoading.value = true;
|
||||
try {
|
||||
const usersRes = await getTenantUserList({ tid });
|
||||
if (usersRes?.code === 200) {
|
||||
tenantUsers.value = usersRes?.data?.list || usersRes?.data || [];
|
||||
} else {
|
||||
tenantUsers.value = [];
|
||||
}
|
||||
} catch {
|
||||
tenantUsers.value = [];
|
||||
} finally {
|
||||
usersLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.tid,
|
||||
(id) => {
|
||||
userSearchKeyword.value = "";
|
||||
if (id == null) {
|
||||
tenantUsers.value = [];
|
||||
return;
|
||||
}
|
||||
loadUsersForTid(id);
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const handleSearchUsers = () => {
|
||||
refreshTenantUsers();
|
||||
};
|
||||
|
||||
const resetUserSearch = () => {
|
||||
userSearchKeyword.value = "";
|
||||
refreshTenantUsers();
|
||||
};
|
||||
|
||||
const handleAddUser = () => {
|
||||
if (props.tid != null) {
|
||||
addUserRef.value?.open(props.tid);
|
||||
}
|
||||
};
|
||||
|
||||
const openPasswordDialog = (row: any) => {
|
||||
currentTenantUserId.value = Number(row?.id || 0) || null;
|
||||
passwordForm.password = "";
|
||||
passwordForm.password2 = "";
|
||||
passwordDialogVisible.value = true;
|
||||
};
|
||||
|
||||
const submitPassword = async () => {
|
||||
if (!passwordFormRef.value || !currentTenantUserId.value) return;
|
||||
|
||||
try {
|
||||
await passwordFormRef.value.validate();
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
passwordSubmitting.value = true;
|
||||
try {
|
||||
const res = await editTenantUser(currentTenantUserId.value, { password: passwordForm.password });
|
||||
if (res.code === 200) {
|
||||
ElMessage.success("密码修改成功");
|
||||
passwordDialogVisible.value = false;
|
||||
} else {
|
||||
ElMessage.error(res.msg || "密码修改失败");
|
||||
}
|
||||
} finally {
|
||||
passwordSubmitting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
refreshTenantUsers,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tenant-users-tab {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.user-search-form {
|
||||
margin-bottom: 8px;
|
||||
padding: 8px 12px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.user-search-form :deep(.el-form-item) {
|
||||
margin-bottom: 8px;
|
||||
margin-top: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user