更新宝贝监护功能
This commit is contained in:
parent
649da2a6fd
commit
eec987041c
3
.env development
Normal file
3
.env development
Normal file
@ -0,0 +1,3 @@
|
||||
# 开发环境接口
|
||||
# VITE_API_BASE_URL='https://backend.yunzer.cn'
|
||||
VITE_API_BASE_URL = 'http://localhost:8000'
|
||||
@ -20,7 +20,7 @@ export function getBabyList() {
|
||||
* @param {number} id 宝贝ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getDetail(id) {
|
||||
export function getBabyDetail(id) {
|
||||
return request({
|
||||
url: `/admin/babys/${id}`,
|
||||
method: "get",
|
||||
@ -32,7 +32,7 @@ export function getDetail(id) {
|
||||
* @param {Object} data 宝贝数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function create(data) {
|
||||
export function createBaby(data) {
|
||||
return request({
|
||||
url: "/admin/babys",
|
||||
method: "post",
|
||||
@ -49,14 +49,11 @@ export function create(data) {
|
||||
* @param {Object} data 更新的数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function update(id, data) {
|
||||
export function editBaby(id, data) {
|
||||
return request({
|
||||
url: `/admin/babys/${id}`,
|
||||
method: "put",
|
||||
data: data,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
method: "post",
|
||||
data: { ...data, _method: 'PUT' }
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,13 +62,39 @@ export function update(id, data) {
|
||||
* @param {number} id 宝贝ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function del(id) {
|
||||
export function deleteBaby(id) {
|
||||
return request({
|
||||
url: `/admin/babys/${id}`,
|
||||
method: "delete",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定父母
|
||||
* @param {number} id 宝贝ID
|
||||
* @param {Object} data 绑定数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function bindParent(id, data) {
|
||||
return request({
|
||||
url: `/admin/babys/bindparents/${id}`,
|
||||
method: "post",
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父母
|
||||
* @param {number} id 宝贝ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getParents(id) {
|
||||
return request({
|
||||
url: `/admin/babys/getParents/${id}`,
|
||||
method: "get",
|
||||
});
|
||||
}
|
||||
|
||||
/*************************************************
|
||||
****************** 用户相关接口 ******************
|
||||
*************************************************/
|
||||
@ -124,7 +147,7 @@ export function createUser(data) {
|
||||
export function updateUser(id, data) {
|
||||
return request({
|
||||
url: `/admin/babyhealthUser/${id}`,
|
||||
method: "put",
|
||||
method: "post",
|
||||
data: data,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
import request from '@/utils/request';
|
||||
|
||||
/**
|
||||
* 上传头像
|
||||
* @param {FormData} data 表单数据
|
||||
* @param {Object} params 查询参数
|
||||
* @returns {Promise} Promise对象
|
||||
*/
|
||||
export function uploadAvatar(data, params) {
|
||||
return request({
|
||||
url: '/admin/uploadavatar',
|
||||
method: 'post',
|
||||
data,
|
||||
params,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {FormData} data 表单数据
|
||||
* @param {Object} params 查询参数
|
||||
* @returns {Promise} Promise对象
|
||||
*/
|
||||
export function uploadFile(data, params) {
|
||||
return request({
|
||||
url: '/admin/upload',
|
||||
method: 'post',
|
||||
data,
|
||||
params,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
uploadAvatar,
|
||||
uploadFile
|
||||
};
|
||||
369
src/views/apps/babyhealth/babys/components/bindParents.vue
Normal file
369
src/views/apps/babyhealth/babys/components/bindParents.vue
Normal file
@ -0,0 +1,369 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="绑定父母"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
|
||||
|
||||
<el-form-item label="父亲">
|
||||
<div class="parent-selector">
|
||||
<el-select
|
||||
v-model="formData.father_id"
|
||||
placeholder="请输入手机号或者账号"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
:remote-method="(query) => handleSearchUser(query, 1)"
|
||||
:loading="searchLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #default>
|
||||
<div class="search-tip">支持账号/手机号搜索</div>
|
||||
<el-option
|
||||
v-for="user in maleUsers"
|
||||
:key="user.id"
|
||||
:label="user.name || user.account"
|
||||
:value="user.id"
|
||||
>
|
||||
<div class="user-option">
|
||||
<span class="user-name">{{ user.name || user.account }}</span>
|
||||
<span class="user-phone">{{ user.phone }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
<el-button link type="primary" @click="handleSearchUser('', 1)">
|
||||
<i class="fa-solid fa-refresh"></i> 刷新列表
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="母亲">
|
||||
<div class="parent-selector">
|
||||
<el-select
|
||||
v-model="formData.mother_id"
|
||||
placeholder="请输入手机号或者账号"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
:remote-method="(query) => handleSearchUser(query, 2)"
|
||||
:loading="searchLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #default>
|
||||
<div class="search-tip">支持账号/手机号搜索</div>
|
||||
<el-option
|
||||
v-for="user in femaleUsers"
|
||||
:key="user.id"
|
||||
:label="user.name || user.account"
|
||||
:value="user.id"
|
||||
>
|
||||
<div class="user-option">
|
||||
<span class="user-name">{{ user.name || user.account }}</span>
|
||||
<span class="user-phone">{{ user.phone }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</template>
|
||||
</el-select>
|
||||
<el-button link type="primary" @click="handleSearchUser('', 2)">
|
||||
<i class="fa-solid fa-refresh"></i> 刷新列表
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, watch, computed } from 'vue';
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { bindParent, getParents, getUserList } from '@/api/babyhealth';
|
||||
|
||||
interface Baby {
|
||||
id: number;
|
||||
name: string;
|
||||
nickname: string;
|
||||
avatar: string;
|
||||
}
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
account: string;
|
||||
phone: string;
|
||||
sex: number;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
visible: boolean;
|
||||
babyData?: Baby | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:visible': [value: boolean];
|
||||
success: [];
|
||||
}>();
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const loading = ref(false);
|
||||
const searchLoading = ref(false);
|
||||
const formRef = ref<FormInstance>();
|
||||
const users = ref<User[]>([]);
|
||||
const allUsers = ref<User[]>([]);
|
||||
const parents = ref<User[]>([]);
|
||||
|
||||
const formData = reactive<{
|
||||
father_id: number | null;
|
||||
mother_id: number | null;
|
||||
}>({
|
||||
father_id: null,
|
||||
mother_id: null,
|
||||
});
|
||||
|
||||
const formRules: FormRules = {};
|
||||
|
||||
// 过滤男性用户
|
||||
const maleUsers = computed(() => {
|
||||
return users.value.filter(user => user.sex === 1);
|
||||
});
|
||||
|
||||
// 过滤女性用户
|
||||
const femaleUsers = computed(() => {
|
||||
return users.value.filter(user => user.sex === 2);
|
||||
});
|
||||
|
||||
// 拼接接口路径
|
||||
const getEnvUrl = (path: string) => {
|
||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
|
||||
return `${API_BASE_URL}${path}`;
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
formData.father_id = null;
|
||||
formData.mother_id = null;
|
||||
};
|
||||
|
||||
// 关闭对话框
|
||||
const handleClose = () => {
|
||||
dialogVisible.value = false;
|
||||
resetForm();
|
||||
};
|
||||
|
||||
// 监听 visible 变化
|
||||
watch(() => props.visible, (val) => {
|
||||
dialogVisible.value = val;
|
||||
if (val) {
|
||||
fetchUsers();
|
||||
}
|
||||
});
|
||||
|
||||
// 监听对话框关闭,重置搜索结果
|
||||
watch(dialogVisible, (val) => {
|
||||
if (!val) {
|
||||
users.value = parents.value;
|
||||
}
|
||||
});
|
||||
|
||||
// 监听 dialogVisible 变化
|
||||
watch(dialogVisible, (val) => {
|
||||
emit('update:visible', val);
|
||||
});
|
||||
|
||||
// 获取用户列表和已有父母数据
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
// 获取所有用户列表
|
||||
const res = await getUserList();
|
||||
if (res.code === 200) {
|
||||
allUsers.value = res.data || [];
|
||||
users.value = res.data || [];
|
||||
}
|
||||
|
||||
// 获取当前宝贝已绑定的父母数据
|
||||
if (props.babyData?.id) {
|
||||
const parentsRes = await getParents(props.babyData.id);
|
||||
if (parentsRes.code === 200 && parentsRes.data) {
|
||||
formData.father_id = parentsRes.data.father_id || null;
|
||||
formData.mother_id = parentsRes.data.mother_id || null;
|
||||
|
||||
// 如果已绑定父亲但不在用户列表中,添加进去以便显示名称
|
||||
if (parentsRes.data.father_id && parentsRes.data.father) {
|
||||
const fatherExists = allUsers.value.some(u => u.id === parentsRes.data.father_id);
|
||||
if (!fatherExists) {
|
||||
allUsers.value.push({
|
||||
id: parentsRes.data.father_id,
|
||||
name: parentsRes.data.father,
|
||||
account: '',
|
||||
phone: '',
|
||||
sex: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 如果已绑定母亲但不在用户列表中,添加进去以便显示名称
|
||||
if (parentsRes.data.mother_id && parentsRes.data.mother) {
|
||||
const motherExists = allUsers.value.some(u => u.id === parentsRes.data.mother_id);
|
||||
if (!motherExists) {
|
||||
allUsers.value.push({
|
||||
id: parentsRes.data.mother_id,
|
||||
name: parentsRes.data.mother,
|
||||
account: '',
|
||||
phone: '',
|
||||
sex: 2
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 更新用户列表
|
||||
users.value = allUsers.value;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
ElMessage.error('获取数据失败');
|
||||
}
|
||||
};
|
||||
|
||||
// 搜索用户
|
||||
const handleSearchUser = async (query: string, sex: number) => {
|
||||
searchLoading.value = true;
|
||||
try {
|
||||
if (!query) {
|
||||
// 如果查询为空,显示所有对应性别的用户
|
||||
users.value = allUsers.value.filter(user => user.sex === sex);
|
||||
} else {
|
||||
// 通过账号或手机号搜索
|
||||
const filteredUsers = allUsers.value.filter(user => {
|
||||
const isTargetSex = user.sex === sex;
|
||||
const matchAccount = user.account?.toLowerCase().includes(query.toLowerCase());
|
||||
const matchPhone = user.phone?.includes(query);
|
||||
const matchName = user.name?.toLowerCase().includes(query.toLowerCase());
|
||||
return isTargetSex && (matchAccount || matchPhone || matchName);
|
||||
});
|
||||
users.value = filteredUsers;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索用户失败:', error);
|
||||
ElMessage.error('搜索用户失败');
|
||||
} finally {
|
||||
searchLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return;
|
||||
if (!props.babyData?.id) {
|
||||
ElMessage.error('宝贝信息不存在');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const submitData: Record<string, any> = {};
|
||||
if (formData.father_id) {
|
||||
submitData.father_id = formData.father_id;
|
||||
}
|
||||
if (formData.mother_id) {
|
||||
submitData.mother_id = formData.mother_id;
|
||||
}
|
||||
|
||||
const res = await bindParent(props.babyData.id, submitData);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('绑定成功');
|
||||
dialogVisible.value = false;
|
||||
resetForm();
|
||||
emit('success');
|
||||
} else {
|
||||
ElMessage.error(res.msg || '绑定失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('绑定失败:', error);
|
||||
ElMessage.error('绑定失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.baby-info-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
background: var(--el-fill-color-light);
|
||||
border-radius: 8px;
|
||||
|
||||
.baby-avatar-mini {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.baby-info-text {
|
||||
flex: 1;
|
||||
|
||||
.baby-name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.baby-nickname {
|
||||
font-size: 14px;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.parent-selector {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.el-select {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.search-tip {
|
||||
padding: 8px 12px;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
background: var(--el-fill-color-lighter);
|
||||
}
|
||||
|
||||
.user-option {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
.user-name {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-phone {
|
||||
margin-left: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -6,90 +6,99 @@
|
||||
:close-on-click-modal="false"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form ref="editFormRef" :model="formData" :rules="formRules" label-width="100px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入姓名" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="昵称" prop="nickname">
|
||||
<el-input v-model="formData.nickname" placeholder="请输入昵称" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formData.sex">
|
||||
<el-radio :value="1">男</el-radio>
|
||||
<el-radio :value="2">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="出生日期" prop="birth">
|
||||
<el-date-picker
|
||||
v-model="formData.birth"
|
||||
type="date"
|
||||
placeholder="选择出生日期"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="身高" prop="height">
|
||||
<el-input-number
|
||||
v-model="formData.height"
|
||||
:min="0"
|
||||
:max="200"
|
||||
:precision="1"
|
||||
:step="0.5"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<span style="margin-left: 8px; color: var(--el-text-color-regular)">cm</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="体重" prop="weight">
|
||||
<el-input-number
|
||||
v-model="formData.weight"
|
||||
:min="0"
|
||||
:max="50"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<span style="margin-left: 8px; color: var(--el-text-color-regular)">kg</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="头像">
|
||||
<div class="dialog-content">
|
||||
<!-- 头像上传区域 -->
|
||||
<div class="avatar-section">
|
||||
<el-upload
|
||||
:action="uploadUrl"
|
||||
:show-file-list="false"
|
||||
:on-success="handleAvatarSuccess"
|
||||
:before-upload="beforeAvatarUpload"
|
||||
:http-request="uploadFile"
|
||||
:headers="uploadHeaders"
|
||||
class="avatar-uploader"
|
||||
>
|
||||
<img v-if="formData.avatar" :src="getEnvUrl(formData.avatar)" class="avatar-preview" />
|
||||
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
<el-image
|
||||
v-if="formData.avatar"
|
||||
:src="getEnvUrl(formData.avatar)"
|
||||
class="avatar-preview"
|
||||
fit="cover"
|
||||
:preview-src-list="[getEnvUrl(formData.avatar)]"
|
||||
:initial-index="0"
|
||||
/>
|
||||
<div v-else class="avatar-placeholder">
|
||||
<el-icon class="avatar-uploader-icon"><Plus /></el-icon>
|
||||
<span class="avatar-tip">点击上传头像</span>
|
||||
</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
||||
<!-- 表单区域 -->
|
||||
<el-form ref="editFormRef" :model="formData" :rules="formRules" label-width="100px">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入姓名" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="小名" prop="nickname">
|
||||
<el-input v-model="formData.nickname" placeholder="请输入小名" clearable />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="性别" prop="sex">
|
||||
<el-radio-group v-model="formData.sex">
|
||||
<el-radio :value="1">男</el-radio>
|
||||
<el-radio :value="2">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="出生日期" prop="birth">
|
||||
<el-date-picker
|
||||
v-model="formData.birth"
|
||||
type="date"
|
||||
placeholder="选择出生日期"
|
||||
style="width: 100%"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="身高" prop="height">
|
||||
<el-input
|
||||
v-model="formData.height"
|
||||
:min="0"
|
||||
:max="200"
|
||||
:precision="1"
|
||||
:step="0.5"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #append>CM</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="体重" prop="weight">
|
||||
<el-input
|
||||
v-model="formData.weight"
|
||||
:min="0"
|
||||
:max="50"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #append>KG</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="1">正常</el-radio>
|
||||
<el-radio :value="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :value="1">正常</el-radio>
|
||||
<el-radio :value="0">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 预览对话框 -->
|
||||
<el-dialog v-model="previewVisible" title="头像预览" width="500px">
|
||||
<img w-full :src="getEnvUrl(previewImageUrl)" alt="Preview Image" style="width: 100%" />
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
@ -97,8 +106,8 @@
|
||||
import { ref, reactive, watch } from 'vue';
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { Plus } from '@element-plus/icons-vue';
|
||||
import { createBaby, updateBaby } from '@/api/babyhealth';
|
||||
import { uploadAvatar } from '@/api/upload';
|
||||
import { createBaby, editBaby } from '@/api/babyhealth';
|
||||
import { uploadAvatar } from '@/api/file';
|
||||
|
||||
interface BabyFormData {
|
||||
id: number;
|
||||
@ -119,12 +128,10 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
'update:visible': [value: boolean];
|
||||
submit: [data: BabyFormData];
|
||||
success: [];
|
||||
}>();
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const previewVisible = ref(false);
|
||||
const previewImageUrl = ref('');
|
||||
const loading = ref(false);
|
||||
const editFormRef = ref<FormInstance>();
|
||||
const fileList = ref<any[]>([]);
|
||||
@ -208,45 +215,44 @@ watch(() => props.editData, (data) => {
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 上传配置
|
||||
const uploadUrl = import.meta.env.VITE_API_BASE_URL + "/admin/uploadavatar";
|
||||
const uploadHeaders = {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
};
|
||||
|
||||
// 头像上传前校验
|
||||
const beforeAvatarUpload = (file: any) => {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isLt5M = file.size / 1024 / 1024 < 5;
|
||||
|
||||
if (!isImage) {
|
||||
ElMessage.error('仅支持图片格式');
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (!isLt5M) {
|
||||
ElMessage.error('图片大小不能超过5MB');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// 上传文件到服务器
|
||||
const uploadFile = async (options: any) => {
|
||||
const { file } = options;
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
const uploadFormData = new FormData();
|
||||
uploadFormData.append('file', file);
|
||||
|
||||
try {
|
||||
// 使用uploadAvatar接口上传文件
|
||||
const response = await uploadAvatar(formData, { cate: 'baby_avatar' });
|
||||
const response = await uploadAvatar(uploadFormData, { cate: 'baby_avatar' });
|
||||
|
||||
if (response.code === 200) {
|
||||
if (response.code === 200 || response.code === 201) {
|
||||
// 更新宝贝头像
|
||||
const avatarUrl = response.data.url || response.data.path || '';
|
||||
formData.avatar = avatarUrl;
|
||||
|
||||
// 如果正在编辑,则更新宝贝信息
|
||||
if (formData.id) {
|
||||
await updateBaby(formData.id, { avatar: avatarUrl });
|
||||
if (response.code === 201) {
|
||||
ElMessage.info("文件已存在,使用已有图片");
|
||||
} else {
|
||||
ElMessage.success("头像上传成功");
|
||||
}
|
||||
|
||||
ElMessage.success("头像上传成功");
|
||||
} else {
|
||||
ElMessage.error(response.msg || "上传失败");
|
||||
}
|
||||
@ -256,23 +262,6 @@ const uploadFile = async (options: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 上传成功
|
||||
const handleAvatarSuccess = (response: any) => {
|
||||
if (response.code === 200) {
|
||||
const avatarUrl = response.data.url || response.data.path || '';
|
||||
formData.avatar = avatarUrl;
|
||||
|
||||
// 如果正在编辑,则更新宝贝信息
|
||||
if (formData.id) {
|
||||
updateBaby(formData.id, { avatar: avatarUrl });
|
||||
}
|
||||
|
||||
ElMessage.success("头像上传成功");
|
||||
} else {
|
||||
ElMessage.error(response.msg || "上传失败");
|
||||
}
|
||||
};
|
||||
|
||||
// 上传相关
|
||||
const handleUploadChange = (file: any) => {
|
||||
if (file.raw) {
|
||||
@ -291,11 +280,6 @@ const handleRemove = (file: any) => {
|
||||
formData.avatar = '';
|
||||
};
|
||||
|
||||
const handlePictureCardPreview = (file: any) => {
|
||||
previewImageUrl.value = file.url;
|
||||
previewVisible.value = true;
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!editFormRef.value) return;
|
||||
@ -311,7 +295,7 @@ const handleSubmit = async () => {
|
||||
uploadFormData.append('cate', 'baby');
|
||||
|
||||
const uploadRes = await uploadAvatar(uploadFormData);
|
||||
if (uploadRes.code === 200 && uploadRes.data?.url) {
|
||||
if ((uploadRes.code === 200 || uploadRes.code === 201) && uploadRes.data?.url) {
|
||||
formData.avatar = uploadRes.data.url;
|
||||
} else {
|
||||
ElMessage.error('图片上传失败');
|
||||
@ -319,10 +303,35 @@ const handleSubmit = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
emit('submit', { ...formData });
|
||||
dialogVisible.value = false;
|
||||
resetForm();
|
||||
let res;
|
||||
if (formData.id) {
|
||||
// 编辑 - 使用普通对象,PUT请求
|
||||
const submitData: Record<string, any> = {};
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (key !== 'id' && formData[key as keyof BabyFormData] !== undefined && formData[key as keyof BabyFormData] !== null) {
|
||||
submitData[key] = formData[key as keyof BabyFormData];
|
||||
}
|
||||
});
|
||||
res = await editBaby(formData.id, submitData);
|
||||
} else {
|
||||
// 新增 - 使用 FormData
|
||||
const submitData = new FormData();
|
||||
Object.keys(formData).forEach(key => {
|
||||
if (key !== 'id' && formData[key as keyof BabyFormData] !== undefined && formData[key as keyof BabyFormData] !== null) {
|
||||
submitData.append(key, String(formData[key as keyof BabyFormData]));
|
||||
}
|
||||
});
|
||||
res = await createBaby(submitData);
|
||||
}
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success(formData.id ? '编辑成功' : '添加成功');
|
||||
dialogVisible.value = false;
|
||||
resetForm();
|
||||
emit('success'); // 通知父组件刷新列表
|
||||
} else {
|
||||
ElMessage.error(res.msg || (formData.id ? '编辑失败' : '添加失败'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
ElMessage.error('提交失败');
|
||||
@ -335,39 +344,80 @@ const handleSubmit = async () => {
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.dialog-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.avatar-section {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 24px;
|
||||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||
}
|
||||
|
||||
.avatar-preview {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 6px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border-radius: 8px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
|
||||
:deep(img) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-uploader {
|
||||
:deep(.el-upload) {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
border: 2px dashed var(--el-border-color);
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
transition: all 0.3s;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--el-fill-color-light);
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
|
||||
.avatar-uploader-icon {
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
text-align: center;
|
||||
line-height: 100px;
|
||||
font-size: 32px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.avatar-tip {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-upload-list__item-thumbnail) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@ -59,6 +59,9 @@
|
||||
{{ baby.status === 1 ? '正常' : '禁用' }}
|
||||
</el-tag>
|
||||
<div class="baby-actions">
|
||||
<el-button size="small" type="primary" link @click="handleBindParents(baby)">
|
||||
<i class="fa-solid fa-link"></i>
|
||||
</el-button>
|
||||
<el-button size="small" type="primary" link @click="handleView(baby)">
|
||||
<i class="fa-solid fa-eye"></i>
|
||||
</el-button>
|
||||
@ -82,7 +85,14 @@
|
||||
<BabyEdit
|
||||
v-model:visible="editDialogVisible"
|
||||
:editData="currentBaby"
|
||||
@submit="handleSubmit"
|
||||
@success="handleEditSuccess"
|
||||
/>
|
||||
|
||||
<!-- 绑定父母对话框 -->
|
||||
<BindParents
|
||||
v-model:visible="bindParentsVisible"
|
||||
:babyData="currentBaby"
|
||||
@success="handleBindParentsSuccess"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -91,7 +101,8 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import BabyEdit from './components/edit.vue';
|
||||
import { getBabyList, getDetail, create, update, del } from '@/api/babyhealth';
|
||||
import BindParents from './components/bindParents.vue';
|
||||
import { getBabyList, deleteBaby } from '@/api/babyhealth';
|
||||
|
||||
interface Baby {
|
||||
id: number;
|
||||
@ -109,6 +120,7 @@ interface Baby {
|
||||
const loading = ref(false);
|
||||
const babyList = ref<Baby[]>([]);
|
||||
const editDialogVisible = ref(false);
|
||||
const bindParentsVisible = ref(false);
|
||||
const currentBaby = ref<Baby | null>(null);
|
||||
|
||||
// 拼接接口路径
|
||||
@ -162,7 +174,7 @@ const handleDelete = (row: Baby) => {
|
||||
.then(async () => {
|
||||
try {
|
||||
loading.value = true;
|
||||
const res = await del(row.id);
|
||||
const res = await deleteBaby(row.id);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('删除成功');
|
||||
fetchBabyList();
|
||||
@ -179,32 +191,20 @@ const handleDelete = (row: Baby) => {
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async (data: any) => {
|
||||
try {
|
||||
if (data.id) {
|
||||
// 编辑
|
||||
const res = await update(data.id, data);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('编辑成功');
|
||||
fetchBabyList();
|
||||
} else {
|
||||
ElMessage.error(res.msg || '编辑失败');
|
||||
}
|
||||
} else {
|
||||
// 添加
|
||||
const res = await create(data);
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('添加成功');
|
||||
fetchBabyList();
|
||||
} else {
|
||||
ElMessage.error(res.msg || '添加失败');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(data.id ? '编辑失败:' : '添加失败:', error);
|
||||
ElMessage.error(data.id ? '编辑失败' : '添加失败');
|
||||
}
|
||||
// 绑定父母
|
||||
const handleBindParents = (row: Baby) => {
|
||||
currentBaby.value = row;
|
||||
bindParentsVisible.value = true;
|
||||
};
|
||||
|
||||
// 编辑/添加成功回调
|
||||
const handleEditSuccess = () => {
|
||||
fetchBabyList();
|
||||
};
|
||||
|
||||
// 绑定父母成功回调
|
||||
const handleBindParentsSuccess = () => {
|
||||
fetchBabyList();
|
||||
};
|
||||
|
||||
// 获取宝贝列表
|
||||
@ -212,7 +212,7 @@ const fetchBabyList = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const res = await getBabyList();
|
||||
console.log('获取宝贝列表响应:', res);
|
||||
// console.log('获取宝贝列表响应:', res);
|
||||
if (res.code === 200) {
|
||||
babyList.value = res.data || [];
|
||||
} else {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user