更新字典功能

This commit is contained in:
2025-11-11 18:03:16 +08:00
parent 65b281fe35
commit 8c5859bad5
12 changed files with 880 additions and 827 deletions
+1 -1
View File
@@ -50,5 +50,5 @@ body {
}
}
.el-form-item__label{
min-width: 60px;
min-width: 80px !important;
}
+179
View File
@@ -0,0 +1,179 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { getDictItemsByCode } from '@/api/dict'
/**
* 字典 Store
*
* 用于全局管理系统字典数据
* 缓存字典数据避免重复请求,提高性能
*
* 使用示例:
* const dictStore = useDictStore()
* const statusDict = await dictStore.getDictItems('user_status')
* const roleDict = dictStore.getDictItemsSync('user_role') // 已加载则同步返回
*/
export const useDictStore = defineStore('dict', () => {
// 字典缓存:{ dictCode: [...items] }
const dictCache = ref({})
// 正在加载的字典代码集合
const loadingCodes = ref(new Set())
/**
* 获取字典项(异步)
* @param {string} code - 字典编码,如 'user_status'
* @returns {Promise<Array>} 字典项数组
*/
async function getDictItems(code) {
// 如果缓存中已有,直接返回
if (dictCache.value[code]) {
return dictCache.value[code]
}
// 避免重复请求:如果已在加载中,等待
if (loadingCodes.value.has(code)) {
// 等待加载完成(最多 5 秒)
return await new Promise((resolve) => {
let count = 0
const timer = setInterval(() => {
if (dictCache.value[code]) {
clearInterval(timer)
resolve(dictCache.value[code])
}
count++
if (count > 50) {
clearInterval(timer)
resolve([])
}
}, 100)
})
}
// 标记为正在加载
loadingCodes.value.add(code)
try {
const res = await getDictItemsByCode(code)
let items = []
// 兼容不同的 API 响应格式
if (res?.data && Array.isArray(res.data)) {
items = res.data
} else if (Array.isArray(res)) {
items = res
} else if (res?.data?.data && Array.isArray(res.data.data)) {
items = res.data.data
}
// 缓存字典项
dictCache.value[code] = items
// console.log(`✅ 字典 [${code}] 已加载,共 ${items.length} 项`)
return items
} catch (error) {
console.error(`❌ 加载字典 [${code}] 失败:`, error)
dictCache.value[code] = []
return []
} finally {
// 移除加载标记
loadingCodes.value.delete(code)
}
}
/**
* 同步获取字典项(如果已缓存)
* @param {string} code - 字典编码
* @returns {Array} 字典项数组,未缓存则返回空数组
*/
function getDictItemsSync(code) {
return dictCache.value[code] || []
}
/**
* 预加载字典(在应用启动时调用)
* @param {Array<string>} codes - 字典编码数组
* @returns {Promise<void>}
*/
async function preloadDicts(codes) {
const promises = codes.map(code => getDictItems(code))
await Promise.all(promises)
}
/**
* 根据字典编码和值获取标签
* @param {string} code - 字典编码
* @param {any} value - 字典值
* @returns {string} 字典标签
*/
function getDictLabel(code, value) {
const items = getDictItemsSync(code)
if (!items.length) {
console.warn(`⚠️ 字典 [${code}] 未加载,无法获取标签`)
return String(value)
}
const item = items.find(i => String(i.dict_value) === String(value) || i.dict_value === value)
return item ? item.dict_label : String(value)
}
/**
* 根据字典编码和标签获取值
* @param {string} code - 字典编码
* @param {string} label - 字典标签
* @returns {any} 字典值
*/
function getDictValue(code, label) {
const items = getDictItemsSync(code)
if (!items.length) {
console.warn(`⚠️ 字典 [${code}] 未加载,无法获取值`)
return null
}
const item = items.find(i => i.dict_label === label)
return item ? item.dict_value : null
}
/**
* 清空缓存
* @param {string} code - 字典编码,不指定则清空所有
*/
function clearCache(code) {
if (code) {
delete dictCache.value[code]
// console.log(`✅ 已清除字典 [${code}] 缓存`)
} else {
dictCache.value = {}
// console.log(`✅ 已清除所有字典缓存`)
}
}
/**
* 刷新字典
* @param {string} code - 字典编码
* @returns {Promise<Array>}
*/
async function refreshDict(code) {
clearCache(code)
return getDictItems(code)
}
/**
* 获取所有已缓存的字典
* @returns {Object}
*/
const allDicts = computed(() => dictCache.value)
return {
dictCache,
loadingCodes,
getDictItems,
getDictItemsSync,
preloadDicts,
getDictLabel,
getDictValue,
clearCache,
refreshDict,
allDicts,
}
})
@@ -30,8 +30,8 @@
{{ tenantData.email || '未设置' }}
</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="tenantData.status === 'enabled' ? 'success' : 'info'">
{{ tenantData.status === 'enabled' ? '启用' : '禁用' }}
<el-tag :type="getTenantStatusType(tenantData.status)">
{{ getTenantStatusText(tenantData.status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="创建时间">
@@ -54,8 +54,13 @@
>
<el-form-item label="审核结果" prop="result">
<el-radio-group v-model="auditForm.result">
<el-radio value="approved">通过</el-radio>
<el-radio value="rejected">拒绝</el-radio>
<el-radio
v-for="item in reviewStatusDict"
:key="item.dict_value"
:value="item.dict_value"
>
{{ item.dict_label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审核意见" prop="comment">
@@ -79,9 +84,10 @@
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed } from 'vue';
import { ref, reactive, watch, computed, onMounted } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { auditTenant } from '@/api/tenant';
import { useDictStore } from '@/stores/dict';
interface Props {
modelValue: boolean;
@@ -107,6 +113,19 @@ const loading = ref(false);
const submitting = ref(false);
const tenantData = ref<any>({});
// 字典数据
const dictStore = useDictStore();
const reviewStatusDict = ref<any[]>([]);
// 获取租户审核状态字典数据
async function fetchReviewStatusDict() {
try {
reviewStatusDict.value = await dictStore.getDictItems('tenants_reviewStatus');
} catch (error) {
console.error('获取租户审核状态字典数据失败:', error);
}
}
// 审核表单
const auditForm = reactive({
result: '',
@@ -134,6 +153,19 @@ function getCurrentUser() {
return 'system';
}
// 租户状态相关方法
function getTenantStatusType(status: string): 'success' | 'info' | 'warning' | 'danger' {
// 注意:audit.vue中租户状态是字符串'enabled'或'disabled',与index.vue/detail.vue中的数字1/0不同
// 这里保持原有逻辑作为兜底方案
return status === 'enabled' ? 'success' : 'info';
}
function getTenantStatusText(status: string): string {
// 注意:audit.vue中租户状态是字符串'enabled'或'disabled',与index.vue/detail.vue中的数字1/0不同
// 这里保持原有逻辑作为兜底方案
return status === 'enabled' ? '启用' : '禁用';
}
// 提交审核
async function handleSubmit() {
if (!auditFormRef.value) return;
@@ -184,6 +216,11 @@ watch(
},
{ immediate: true, deep: true }
);
// 在组件挂载时获取字典数据
onMounted(() => {
fetchReviewStatusDict();
});
</script>
<style lang="less" scoped>
@@ -32,8 +32,8 @@
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="状态">
<el-tag :type="tenantData.status === 1 ? 'success' : 'info'">
{{ tenantData.status === 1 ? '启用' : '禁用' }}
<el-tag :type="getTenantStatusType(tenantData.status)">
{{ getTenantStatusText(tenantData.status) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="存储容量">
@@ -66,9 +66,10 @@
</template>
<script setup lang="ts">
import { ref, watch, computed } from 'vue';
import { ref, watch, computed, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { getTenantDetail } from '@/api/tenant';
import { useDictStore } from '@/stores/dict';
interface Props {
modelValue: boolean;
@@ -92,8 +93,40 @@ const visible = computed({
const loading = ref(false);
const tenantData = ref<any>({});
// 字典数据
const dictStore = useDictStore();
const reviewStatusDict = ref<any[]>([]);
const tenantStatusDict = ref<any[]>([]);
// 获取租户审核状态字典数据
async function fetchReviewStatusDict() {
try {
reviewStatusDict.value = await dictStore.getDictItems('tenants_reviewStatus');
} catch (error) {
console.error('获取租户审核状态字典数据失败:', error);
}
}
// 获取租户状态字典数据
async function fetchTenantStatusDict() {
try {
tenantStatusDict.value = await dictStore.getDictItems('tenants_status');
} catch (error) {
console.error('获取租户状态字典数据失败:', error);
}
}
// 审核状态相关方法
function getAuditStatusType(status: string): 'warning' | 'success' | 'danger' | 'info' {
// 使用字典数据
const dictItem = reviewStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_tag_type || 'info';
}
// 兜底方案,保持原有逻辑
const statusMap: Record<string, 'warning' | 'success' | 'danger' | 'info'> = {
pending: 'warning',
approved: 'success',
@@ -103,6 +136,15 @@ function getAuditStatusType(status: string): 'warning' | 'success' | 'danger' |
}
function getAuditStatusText(status: string) {
// 使用字典数据
const dictItem = reviewStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_label || String(status);
}
// 兜底方案,保持原有逻辑
const statusMap: Record<string, string> = {
pending: '待审核',
approved: '已通过',
@@ -111,6 +153,33 @@ function getAuditStatusText(status: string) {
return statusMap[status] || '未知';
}
// 租户状态相关方法
function getTenantStatusType(status: string | number): 'success' | 'info' | 'warning' | 'danger' {
// 使用字典数据
const dictItem = tenantStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_tag_type || 'info';
}
// 兜底方案,保持原有逻辑
return String(status) === '1' ? 'success' : 'info';
}
function getTenantStatusText(status: string | number): string {
// 使用字典数据
const dictItem = tenantStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_label || String(status);
}
// 兜底方案,保持原有逻辑
return String(status) === '1' ? '启用' : '禁用';
}
// 格式化容量显示(后端返回的是 MB,直接格式化)
function formatCapacity(mb: number | undefined | null): string {
if (mb === null || mb === undefined) {
@@ -184,6 +253,12 @@ watch(
{ immediate: true }
);
// 在组件挂载时获取字典数据
onMounted(() => {
fetchReviewStatusDict();
fetchTenantStatusDict();
});
watch(
() => props.tenantId,
() => {
+37 -8
View File
@@ -29,8 +29,12 @@
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="tenantForm.status" placeholder="请选择状态" style="width: 100%">
<el-option label="启用" :value="1" />
<el-option label="禁用" :value="0" />
<el-option
v-for="item in tenantStatusDict"
:key="item.dict_value"
:label="item.dict_label"
:value="item.dict_value"
/>
</el-select>
</el-form-item>
<el-form-item label="存储容量" prop="capacity">
@@ -63,9 +67,15 @@
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed } from 'vue';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { ref, reactive, watch, computed, onMounted, type Ref } from 'vue';
import {
ElMessage,
ElMessageBox,
type FormInstance,
type FormRules,
} from 'element-plus';
import { createTenant, updateTenant } from '@/api/tenant';
import { useDictStore } from '@/stores/dict';
interface Props {
modelValue: boolean;
@@ -90,6 +100,19 @@ const visible = computed({
const submitting = ref(false);
const tenantFormRef = ref<FormInstance>();
// 字典数据
const dictStore = useDictStore();
const tenantStatusDict = ref<any[]>([]);
// 获取租户状态字典数据
async function fetchTenantStatusDict() {
try {
tenantStatusDict.value = await dictStore.getDictItems('tenants_status');
} catch (error) {
console.error('获取租户状态字典数据失败:', error);
}
}
// 判断是否为编辑模式
const isEditing = computed(() => {
return !!(props.tenant && props.tenant.id);
@@ -103,7 +126,7 @@ const tenantForm = reactive({
owner: '',
phone: '',
email: '',
status: 1,
status: '1', // 改为字符串类型,与字典数据中的dict_value保持一致
capacity: 0,
remark: '',
});
@@ -131,7 +154,7 @@ function resetForm() {
tenantForm.owner = '';
tenantForm.phone = '';
tenantForm.email = '';
tenantForm.status = 1;
tenantForm.status = '1'; // 改为字符串类型,与字典数据中的dict_value保持一致
tenantForm.capacity = 0;
tenantForm.remark = '';
tenantFormRef.value?.resetFields();
@@ -146,7 +169,8 @@ function initFormData() {
tenantForm.owner = props.tenant.owner || '';
tenantForm.phone = props.tenant.phone || '';
tenantForm.email = props.tenant.email || '';
tenantForm.status = props.tenant.status != null ? Number(props.tenant.status) : 1;
// 确保status是字符串类型,与字典数据中的dict_value保持一致
tenantForm.status = props.tenant.status != null ? String(props.tenant.status) : '1';
// capacity 后端返回的是 MB,直接使用
tenantForm.capacity = props.tenant.capacity != null ? Number(props.tenant.capacity) : 0;
tenantForm.remark = props.tenant.remark || '';
@@ -168,7 +192,7 @@ async function handleSubmit() {
owner: tenantForm.owner,
phone: tenantForm.phone || '',
email: tenantForm.email || '',
status: Number(tenantForm.status),
status: Number(tenantForm.status), // 提交时转换为数字类型
// capacity 前后端都使用 MB
capacity: tenantForm.capacity || 0,
remark: tenantForm.remark || '',
@@ -215,6 +239,11 @@ watch(
{ immediate: true }
);
// 在组件挂载时获取字典数据
onMounted(() => {
fetchTenantStatusDict();
});
watch(
() => props.tenant,
() => {
+74 -2
View File
@@ -70,8 +70,8 @@
</el-table-column>
<el-table-column label="状态" width="80">
<template #default="{ row }">
<el-tag :type="row.status == 1 ? 'success' : 'info'">
{{ row.status == 1 ? '启用' : '禁用' }}
<el-tag :type="getTenantStatusType(row.status)">
{{ getTenantStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
@@ -151,10 +151,15 @@ import { ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Plus, Check, View, Edit, Delete, Refresh } from '@element-plus/icons-vue';
import { getAllTenants, deleteTenant } from '@/api/tenant';
import { useDictStore } from '@/stores/dict';
import TenantDetail from './components/detail.vue';
import TenantAudit from './components/audit.vue';
import TenantEdit from './components/edit.vue';
// 字典数据
const reviewStatusDict = ref<any[]>([]);
const tenantStatusDict = ref<any[]>([]);
const tenants = ref<any[]>([]);
const loading = ref(false);
const error = ref('');
@@ -162,6 +167,26 @@ const page = ref(1);
const pageSize = ref(10);
const total = ref(0);
// 获取审核状态字典
const fetchReviewStatusDict = async () => {
try {
const dictStore = useDictStore();
reviewStatusDict.value = await dictStore.getDictItems('tenants_reviewStatus');
} catch (err) {
console.error('获取租户审核状态字典失败:', err);
}
};
// 获取租户状态字典
const fetchTenantStatusDict = async () => {
try {
const dictStore = useDictStore();
tenantStatusDict.value = await dictStore.getDictItems('tenants_status');
} catch (err) {
console.error('获取租户状态字典失败:', err);
}
};
// 弹窗状态
const showViewDialog = ref(false);
const showAuditDialog = ref(false);
@@ -218,6 +243,15 @@ async function refresh() {
function getAuditStatusType(
status: string
): 'warning' | 'success' | 'danger' | 'info' {
// 使用字典数据
const dictItem = reviewStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_tag_type || 'info';
}
// 兜底方案,保持原有逻辑
const statusMap: Record<string, 'warning' | 'success' | 'danger' | 'info'> =
{
pending: 'warning',
@@ -228,6 +262,15 @@ function getAuditStatusType(
}
function getAuditStatusText(status: string) {
// 使用字典数据
const dictItem = reviewStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_label || String(status);
}
// 兜底方案,保持原有逻辑
const statusMap: Record<string, string> = {
pending: '待审核',
approved: '已通过',
@@ -236,6 +279,33 @@ function getAuditStatusText(status: string) {
return statusMap[status] || '未知';
}
// 租户状态相关方法
function getTenantStatusType(status: string | number): 'success' | 'info' | 'warning' | 'danger' {
// 使用字典数据
const dictItem = tenantStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_tag_type || 'info';
}
// 兜底方案,保持原有逻辑
return String(status) === '1' ? 'success' : 'info';
}
function getTenantStatusText(status: string | number): string {
// 使用字典数据
const dictItem = tenantStatusDict.value.find(
(item: any) => String(item.dict_value) === String(status)
);
if (dictItem) {
return dictItem.dict_label || String(status);
}
// 兜底方案,保持原有逻辑
return String(status) === '1' ? '启用' : '禁用';
}
// 格式化容量显示(后端返回的是 MB,直接格式化)
function formatCapacity(mb: number | string | undefined | null): string {
// 处理 null/undefined/空字符串,确保是数字类型
@@ -335,6 +405,8 @@ async function handleDelete(row: any) {
onMounted(() => {
fetchTenants();
fetchReviewStatusDict();
fetchTenantStatusDict();
});
</script>
+42 -123
View File
@@ -25,43 +25,6 @@
<el-input v-model="form.email" />
</el-form-item>
<!-- 部门 -->
<el-form-item label="部门">
<el-select
v-model="form.department_id"
placeholder="请选择部门"
style="width: 100%"
:loading="loadingDepartments"
clearable
@change="handleDepartmentChange"
>
<el-option
v-for="dept in departmentList"
:key="dept.id"
:label="dept.name"
:value="dept.id"
/>
</el-select>
</el-form-item>
<!-- 职位 -->
<el-form-item label="职位">
<el-select
v-model="form.position_id"
placeholder="请选择职位"
style="width: 100%"
:loading="loadingPositions"
clearable
>
<el-option
v-for="pos in positionList"
:key="pos.id"
:label="pos.name"
:value="pos.id"
/>
</el-select>
</el-form-item>
<!-- 角色 -->
<el-form-item label="角色">
<el-select
@@ -86,7 +49,11 @@
<!-- 状态 -->
<el-form-item label="状态">
<el-select v-model="form.status">
<el-select
v-model="form.status"
placeholder="请选择状态"
style="width: 100%"
>
<el-option
v-for="item in statusDict"
:key="item.dict_value"
@@ -106,17 +73,19 @@
</template>
<script setup lang="ts">
import { ref, computed, watch } from "vue";
import { ref, computed, watch, onMounted } from "vue";
import { ElMessage } from "element-plus";
import {
addUser,
editUser,
getUserInfo,
} from "@/api/user";
import { getTenantPositions, getPositionsByDepartment } from "@/api/position";
import { useAuthStore } from "@/stores/auth";
import { useDictStore } from "@/stores/dict";
const authStore = useAuthStore();
const dictStore = useDictStore();
const props = defineProps({
modelValue: {
@@ -131,44 +100,23 @@ const props = defineProps({
type: Array,
default: () => [],
},
departmentList: {
type: Array,
default: () => [],
},
positionList: {
type: Array,
default: () => [],
},
loadingRoles: {
type: Boolean,
default: false,
},
loadingDepartments: {
type: Boolean,
default: false,
},
loadingPositions: {
type: Boolean,
default: false,
},
statusDict: {
type: Array,
default: () => [],
},
tenantId: {
type: Number,
default: 0,
},
});
const emit = defineEmits(['update:modelValue', 'submit', 'close', 'fetch-positions']);
const emit = defineEmits(['update:modelValue', 'submit', 'close']);
const visible = ref(false);
const formRef = ref(null);
const loadingRoles = ref(false);
const loadingDepartments = ref(false);
const loadingPositions = ref(false);
const isAdd = ref(false);
const statusDict = ref([]);
const form = ref<any>({
id: null,
@@ -177,10 +125,8 @@ const form = ref<any>({
password: "",
email: "",
role: null,
status: "active",
status: "1", // ✅ 改为字典中的值"1"(启用)而不是"active"
tenant_id: null,
department_id: null,
position_id: null,
});
const dialogTitle = computed(() => {
@@ -199,10 +145,31 @@ watch(visible, (newVal) => {
}
});
// 监听 statusDict 变化,用于调试
watch(() => props.statusDict, (newVal) => {
console.log('UserEdit statusDict updated:', newVal, 'Length:', Array.isArray(newVal) ? newVal.length : 'not array');
}, { immediate: true, deep: true });
const getCurrentTenantId = () => {
return props.tenantId || 0;
};
// 获取状态字典数据
const fetchStatusDict = async () => {
try {
const dictItems = await dictStore.getDictItems('user_status');
statusDict.value = dictItems || [];
} catch (error) {
console.error("获取用户状态字典失败:", error);
statusDict.value = [];
}
};
// 组件挂载时获取字典数据
onMounted(() => {
fetchStatusDict();
});
const loadUserData = async (user: any) => {
try {
// 处理两种调用方式:传递用户对象或用户 ID
@@ -220,12 +187,11 @@ const loadUserData = async (user: any) => {
const tenantId = getCurrentTenantId();
let roleValue = data.role || null;
let statusStr = "active";
if (typeof data.status === 'number') {
statusStr = data.status === 1 ? "active" : "inactive";
} else if (typeof data.status === 'string') {
statusStr = data.status;
}
// ✅ 直接使用后端返回的状态值,不转换
// 字典中的值就是 "1"(启用)、"0"(禁用)等
const statusValue = data.status !== undefined && data.status !== null
? String(data.status)
: "1";
form.value = {
id: data.id,
@@ -234,16 +200,11 @@ const loadUserData = async (user: any) => {
password: "",
email: data.email,
role: roleValue,
department_id: data.department_id || null,
position_id: data.position_id || null,
status: statusStr,
status: statusValue, // ✅ 使用字典中的原始值
tenant_id: data.tenant_id || tenantId,
};
// 如果用户有部门,根据部门加载职位
if (form.value.department_id) {
await loadPositions(form.value.department_id);
}
} catch (e: any) {
console.error('Failed to load user data:', e);
const errorMsg = e?.response?.data?.message || e?.message || "加载用户失败";
@@ -252,31 +213,7 @@ const loadUserData = async (user: any) => {
}
};
const loadPositions = async (departmentId?: number) => {
loadingPositions.value = true;
try {
const tenantId = getCurrentTenantId();
let res;
if (departmentId && departmentId > 0) {
res = await getPositionsByDepartment(departmentId);
} else {
res = await getTenantPositions(tenantId);
}
// 职位列表由父组件通过 prop 传入,这里仅用于加载示例
} catch (error: any) {
console.error('获取职位列表失败:', error);
} finally {
loadingPositions.value = false;
}
};
const handleDepartmentChange = (departmentId: number | null) => {
form.value.position_id = null;
if (departmentId && departmentId > 0) {
// 让父组件加载对应部门的职位
emit('fetch-positions', departmentId);
}
};
const handleCancel = () => {
visible.value = false;
@@ -290,10 +227,8 @@ const handleClose = () => {
password: "",
email: "",
role: null,
status: "active",
status: "1",
tenant_id: null,
department_id: null,
position_id: null,
};
isAdd.value = false;
emit('close');
@@ -315,13 +250,6 @@ const handleSubmit = async () => {
submitData.role = form.value.role;
}
if (form.value.department_id) {
submitData.department_id = form.value.department_id;
}
if (form.value.position_id) {
submitData.position_id = form.value.position_id;
}
if (form.value.tenant_id) {
submitData.tenant_id = form.value.tenant_id;
}
@@ -347,13 +275,6 @@ const handleSubmit = async () => {
submitData.role = form.value.role;
}
if (form.value.department_id) {
submitData.department_id = form.value.department_id;
}
if (form.value.position_id) {
submitData.position_id = form.value.position_id;
}
if (form.value.tenant_id) {
submitData.tenant_id = form.value.tenant_id;
}
@@ -382,10 +303,8 @@ defineExpose({
password: "",
email: "",
role: null,
status: "active",
status: "1",
tenant_id: tenantId || getCurrentTenantId(),
department_id: null,
position_id: null,
};
visible.value = true;
},
@@ -32,16 +32,7 @@
align="center"
min-width="200"
/>
<el-table-column prop="department" label="部门" width="150" align="center">
<template #default="scope">
<span>{{ scope.row.departmentName || '未分配' }}</span>
</template>
</el-table-column>
<el-table-column prop="position" label="职位" width="150" align="center">
<template #default="scope">
<span>{{ scope.row.positionName || '未分配' }}</span>
</template>
</el-table-column>
<el-table-column prop="role" label="角色" width="150" align="center">
<template #default="scope">
<el-tag :type="getRoleTagType(scope.row.roleName)">
@@ -116,8 +107,6 @@ import {
getUserInfo,
} from "@/api/user";
import { getRoleByTenantId, getAllRoles } from "@/api/role";
import { getTenantDepartments } from "@/api/department";
import { getTenantPositions } from "@/api/position";
import { getDictItemsByCode } from '@/api/dict'
import { useAuthStore } from "@/stores/auth";
@@ -140,14 +129,6 @@ const props = defineProps({
type: Array,
default: () => [],
},
departmentList: {
type: Array,
default: () => [],
},
positionList: {
type: Array,
default: () => [],
},
statusDict: {
type: Array,
default: () => [],
@@ -235,22 +216,6 @@ const fetchUsers = async () => {
roleName = roleInfo ? roleInfo.roleName : '';
}
// 查找部门名称
let departmentName = '';
const departmentId = item.department_id || null;
if (departmentId) {
const deptInfo = (props.departmentList as any[]).find(d => d.id === departmentId);
departmentName = deptInfo ? deptInfo.name : '';
}
// 查找职位名称
let positionName = '';
const positionId = item.position_id || null;
if (positionId) {
const posInfo = (props.positionList as any[]).find(p => p.id === positionId);
positionName = posInfo ? posInfo.name : '';
}
// 处理时间字段
const lastLoginTime = item.last_login_time || item.lastLoginTime || null;
const lastLoginIp = item.last_login_ip || item.lastLoginIp || null;
@@ -262,10 +227,6 @@ const fetchUsers = async () => {
email: item.email,
role: roleValue,
roleName: roleName,
department_id: departmentId,
departmentName: departmentName,
position_id: positionId,
positionName: positionName,
status: item.status,
lastLoginTime: lastLoginTime
? new Date(lastLoginTime).toLocaleString("zh-CN", {
+12 -97
View File
@@ -36,16 +36,7 @@
align="center"
min-width="200"
/>
<el-table-column prop="department" label="部门" width="150" align="center">
<template #default="scope">
<span>{{ scope.row.departmentName || '未分配' }}</span>
</template>
</el-table-column>
<el-table-column prop="position" label="职位" width="150" align="center">
<template #default="scope">
<span>{{ scope.row.positionName || '未分配' }}</span>
</template>
</el-table-column>
<el-table-column prop="role" label="角色" width="150" align="center">
<template #default="scope">
<el-tag :type="getRoleTagType(scope.row.roleName)">
@@ -114,11 +105,7 @@
@update:modelValue="editDialogVisible = $event"
:is-edit="isEdit"
:role-list="roleList"
:department-list="departmentList"
:position-list="positionList"
:loading-roles="loadingRoles"
:loading-departments="loadingDepartments"
:loading-positions="loadingPositions"
:status-dict="statusDict"
:tenant-id="getCurrentTenantId()"
@submit="handleEditSuccess"
@@ -147,10 +134,8 @@ import {
getUserInfo,
} from "@/api/user";
import { getRoleByTenantId, getAllRoles } from "@/api/role";
import { getTenantDepartments } from "@/api/department";
import { getTenantPositions, getPositionsByDepartment } from "@/api/position";
import { getDictItemsByCode } from '@/api/dict'
import { useAuthStore } from "@/stores/auth";
import { useDictStore } from "@/stores/dict";
import UserEditDialog from './components/UserEdit.vue'
import ChangePasswordDialog from './components/ChangePassword.vue'
@@ -166,6 +151,7 @@ interface User {
}
const authStore = useAuthStore();
const dictStore = useDictStore();
const page = ref(1);
const pageSize = ref(10);
@@ -174,10 +160,6 @@ const total = ref(0);
const users = ref<any[]>([]);
const roleList = ref<any[]>([]);
const loadingRoles = ref(false);
const departmentList = ref<any[]>([]);
const loadingDepartments = ref(false);
const positionList = ref<any[]>([]);
const loadingPositions = ref(false);
const loading = ref(false);
// 状态字典
@@ -196,12 +178,10 @@ const changePasswordRef = ref()
const fetchStatusDict = async () => {
try {
const res = await getDictItemsByCode('user_status');
let items: any[] = [];
if (res?.data && Array.isArray(res.data)) items = res.data;
else if (Array.isArray(res)) items = res;
const items = await dictStore.getDictItems('user_status');
statusDict.value = items;
} catch (err) {
console.error('Error fetching status dict:', err);
statusDict.value = [];
}
}
@@ -265,48 +245,7 @@ const fetchRoles = async () => {
}
};
// 获取部门列表
const fetchDepartments = async () => {
loadingDepartments.value = true;
try {
const tenantId = getCurrentTenantId();
const res = await getTenantDepartments(tenantId);
if (res.code === 0 && res.data) {
departmentList.value = Array.isArray(res.data) ? res.data : [];
} else {
departmentList.value = [];
}
} catch (error: any) {
console.error('获取部门列表失败:', error);
departmentList.value = [];
} finally {
loadingDepartments.value = false;
}
};
// 获取职位列表
const fetchPositions = async (departmentId?: number) => {
loadingPositions.value = true;
try {
const tenantId = getCurrentTenantId();
let res;
if (departmentId && departmentId > 0) {
res = await getPositionsByDepartment(departmentId);
} else {
res = await getTenantPositions(tenantId);
}
if (res.code === 0 && res.data) {
positionList.value = Array.isArray(res.data) ? res.data : [];
} else {
positionList.value = [];
}
} catch (error: any) {
console.error('获取职位列表失败:', error);
positionList.value = [];
} finally {
loadingPositions.value = false;
}
};
// 获取角色标签类型
const getRoleTagType = (roleName: string) => {
@@ -318,8 +257,10 @@ const getRoleTagType = (roleName: string) => {
// 根据状态值获取字典显示文本
const getStatusLabel = (status: any) => {
// 确保状态值为字符串类型进行比较
const sval = status !== undefined && status !== null ? String(status) : '';
const item = statusDict.value.find((d: any) => String(d.dict_value) === sval || d.dict_value === status);
// 在字典中查找匹配项
const item = statusDict.value.find((d: any) => String(d.dict_value) === sval);
if (item && item.dict_label) return item.dict_label;
// 兼容旧逻辑
if (status === 1 || sval === '1' || sval === 'active') return '启用';
@@ -361,19 +302,8 @@ const fetchUsers = async () => {
roleName = roleInfo ? roleInfo.roleName : '';
}
let departmentName = '';
const departmentId = item.department_id || null;
if (departmentId) {
const deptInfo = departmentList.value.find(d => d.id === departmentId);
departmentName = deptInfo ? deptInfo.name : '';
}
let positionName = '';
const positionId = item.position_id || null;
if (positionId) {
const posInfo = positionList.value.find(p => p.id === positionId);
positionName = posInfo ? posInfo.name : '';
}
// 确保状态值正确处理
const statusValue = item.status !== undefined && item.status !== null ? item.status : '1';
const lastLoginTime = item.last_login_time || item.lastLoginTime || null;
const lastLoginIp = item.last_login_ip || item.lastLoginIp || null;
@@ -385,11 +315,7 @@ const fetchUsers = async () => {
email: item.email,
role: roleValue,
roleName: roleName,
department_id: departmentId,
departmentName: departmentName,
position_id: positionId,
positionName: positionName,
status: item.status,
status: statusValue, // 使用处理后的状态值
lastLoginTime: lastLoginTime
? new Date(lastLoginTime).toLocaleString("zh-CN", {
year: "numeric",
@@ -416,8 +342,6 @@ const fetchUsers = async () => {
onMounted(async () => {
await Promise.all([
fetchRoles(),
fetchDepartments(),
fetchPositions(),
fetchStatusDict(),
]);
fetchUsers();
@@ -432,8 +356,6 @@ const refresh = async () => {
try {
await Promise.all([
fetchRoles(),
fetchDepartments(),
fetchPositions(),
fetchStatusDict(),
]);
await fetchUsers();
@@ -480,14 +402,7 @@ const handleEditSuccess = () => {
fetchUsers();
};
// 职位加载回调
const handleFetchPositions = (departmentId: number | null) => {
if (departmentId && departmentId > 0) {
fetchPositions(departmentId);
} else {
fetchPositions();
}
};
// 删除用户
const handleDelete = async (user: User) => {