yunzer_go/pc/src/views/system/tenant/components/edit.vue
2025-11-13 11:28:00 +08:00

353 lines
9.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
:title="isEditing ? '编辑租户' : '添加租户'"
v-model="visible"
width="500px"
:close-on-click-modal="false"
@close="handleClose"
>
<el-form
:model="tenantForm"
:rules="formRules"
ref="tenantFormRef"
label-width="90px"
>
<el-form-item label="租户名称" prop="name">
<el-input v-model="tenantForm.name" placeholder="请输入租户名称" />
</el-form-item>
<el-form-item label="负责人" prop="owner">
<el-input v-model="tenantForm.owner" placeholder="请输入负责人" />
</el-form-item>
<el-form-item label="联系电话" prop="phone">
<el-input v-model="tenantForm.phone" placeholder="请输入联系电话" />
</el-form-item>
<el-form-item label="邮箱" prop="email">
<el-input v-model="tenantForm.email" placeholder="请输入邮箱" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="tenantForm.status" placeholder="请选择状态" style="width: 100%">
<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">
<el-input-number
v-model="tenantForm.capacity"
:min="0"
:precision="2"
:step="100"
style="width: 100%"
placeholder="请输入存储容量MB"
/>
<div class="form-tip">单位MB1GB = 1024MB</div>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="tenantForm.remark"
type="textarea"
:rows="3"
placeholder="请输入备注"
/>
</el-form-item>
<el-form-item label="附件">
<el-upload
:limit="1"
list-type="picture-card"
:file-list="uploadFileList"
:before-upload="beforeUpload"
:http-request="handleCustomUpload"
:on-success="handleUploadSuccess"
:on-remove="handleRemove"
:on-preview="handlePreview"
accept="image/*"
>
<el-icon><Plus /></el-icon>
</el-upload>
<el-dialog v-model="previewVisible" width="500px">
<img :src="previewUrl" style="width: 100%" />
</el-dialog>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="handleClose">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitting">
保存
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed, onMounted, type Ref } from 'vue';
import {
ElMessage,
ElMessageBox,
type FormInstance,
type FormRules,
type UploadFile,
type UploadUserFile,
type UploadRequestOptions,
} from 'element-plus';
import { Plus } from '@element-plus/icons-vue';
import { createTenant, updateTenant } from '@/api/tenant';
import { uploadFile } from '@/api/file';
import request from '@/utils/request';
import { useDictStore } from '@/stores/dict';
interface Props {
modelValue: boolean;
tenant?: any;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
tenant: null,
});
const emit = defineEmits<{
'update:modelValue': [value: boolean];
success: [];
}>();
const visible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val),
});
const submitting = ref(false);
const tenantFormRef = ref<FormInstance>();
const uploadFileList = ref<UploadUserFile[]>([]);
const previewVisible = ref(false);
const previewUrl = ref('');
// 字典数据
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);
});
// 表单数据
const tenantForm = reactive({
id: null as number | null,
name: '',
owner: '',
phone: '',
email: '',
status: '1', // 改为字符串类型与字典数据中的dict_value保持一致
capacity: 0,
remark: '',
attachment_url: '' as string,
});
const formRules: FormRules = {
name: [{ required: true, message: '请输入租户名称', trigger: 'blur' }],
owner: [{ required: true, message: '请输入负责人', trigger: 'blur' }],
phone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' },
],
email: [{ type: 'email', message: '请输入正确的邮箱', trigger: 'blur' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
capacity: [
{ required: true, message: '请输入存储容量', trigger: 'blur' },
{ type: 'number', min: 0, message: '存储容量不能小于0', trigger: 'blur' },
],
};
function resolveFileUrl(path: string): string {
if (!path) return '';
if (/^https?:\/\//i.test(path)) return path;
const base = (request as any)?.defaults?.baseURL || '';
try {
const origin = new URL(base, window.location.origin).origin;
return origin.replace(/\/+$/, '') + '/' + path.replace(/^\/+/, '');
} catch {
return path;
}
}
// 重置表单
function resetForm() {
tenantForm.id = null;
tenantForm.name = '';
tenantForm.owner = '';
tenantForm.phone = '';
tenantForm.email = '';
tenantForm.status = '1'; // 改为字符串类型与字典数据中的dict_value保持一致
tenantForm.capacity = 0;
tenantForm.remark = '';
tenantForm.attachment_url = '';
uploadFileList.value = [];
tenantFormRef.value?.resetFields();
}
// 初始化表单数据
function initFormData() {
if (props.tenant && props.tenant.id) {
tenantForm.id = props.tenant.id;
tenantForm.name = props.tenant.name || '';
tenantForm.owner = props.tenant.owner || '';
tenantForm.phone = props.tenant.phone || '';
tenantForm.email = props.tenant.email || '';
// 确保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 || '';
tenantForm.attachment_url = props.tenant.attachment_url || '';
uploadFileList.value = tenantForm.attachment_url
? [{ name: '附件', url: resolveFileUrl(tenantForm.attachment_url) }]
: [];
} else {
resetForm();
}
}
// 提交表单
async function handleSubmit() {
if (!tenantFormRef.value) return;
await tenantFormRef.value.validate();
submitting.value = true;
try {
const tenantData = {
name: tenantForm.name,
owner: tenantForm.owner,
phone: tenantForm.phone || '',
email: tenantForm.email || '',
status: Number(tenantForm.status), // 提交时转换为数字类型
// capacity 前后端都使用 MB
capacity: tenantForm.capacity || 0,
remark: tenantForm.remark || '',
attachment_url: tenantForm.attachment_url || '',
};
let res;
if (isEditing.value && tenantForm.id) {
res = await updateTenant(tenantForm.id, tenantData);
} else {
res = await createTenant(tenantData);
}
if (res.success) {
if (isEditing.value) {
ElMessage.success('租户信息已更新');
} else {
ElMessage.success('租户添加成功');
}
handleClose();
emit('success');
} else {
ElMessage.error(res.message || '操作失败,请重试');
}
} catch (err: any) {
ElMessage.error(err.message || '操作失败,请重试');
} finally {
submitting.value = false;
}
}
const handleClose = () => {
visible.value = false;
resetForm();
};
// 监听 tenant 变化,初始化表单
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
initFormData();
}
},
{ immediate: true }
);
// 在组件挂载时获取字典数据
onMounted(() => {
fetchTenantStatusDict();
});
watch(
() => props.tenant,
() => {
if (visible.value) {
initFormData();
}
},
{ deep: true }
);
const beforeUpload = (file: File) => {
const isImage = file.type.startsWith('image/');
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isImage) {
ElMessage.error('仅支持图片类型');
return false;
}
if (!isLt5M) {
ElMessage.error('图片大小不能超过 5MB');
return false;
}
return true;
};
const handleUploadSuccess = (response: any, file: UploadFile) => {
const raw = response?.data?.file_url || response?.data?.url || response?.url || '';
if (raw) {
tenantForm.attachment_url = raw;
const abs = resolveFileUrl(raw);
uploadFileList.value = [{ name: file.name, url: abs }];
ElMessage.success('上传成功');
} else {
ElMessage.error('上传成功但未返回URL');
}
};
const handleRemove = () => {
tenantForm.attachment_url = '';
uploadFileList.value = [];
};
const handlePreview = (file: UploadFile) => {
previewUrl.value = (file.url as string) || tenantForm.attachment_url || '';
if (previewUrl.value) previewVisible.value = true;
};
const handleCustomUpload = async (options: UploadRequestOptions) => {
const { file, onError, onSuccess } = options as any;
try {
const formData = new FormData();
formData.append('file', file as File);
const res = await uploadFile(formData, { category: '图片' });
onSuccess && onSuccess(res);
} catch (err) {
ElMessage.error('上传失败');
onError && onError(err);
}
};
</script>
<style lang="less" scoped>
.form-tip {
font-size: 12px;
color: var(--el-text-color-placeholder);
margin-top: 4px;
}
</style>