更新字典功能
This commit is contained in:
@@ -50,5 +50,5 @@ body {
|
||||
}
|
||||
}
|
||||
.el-form-item__label{
|
||||
min-width: 60px;
|
||||
min-width: 80px !important;
|
||||
}
|
||||
@@ -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,
|
||||
() => {
|
||||
|
||||
@@ -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,
|
||||
() => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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", {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user