add
This commit is contained in:
3
frontend/package-lock.json
generated
3
frontend/package-lock.json
generated
@@ -1429,6 +1429,7 @@
|
||||
"version": "5.9.3",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -1448,6 +1449,7 @@
|
||||
"version": "5.4.21",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.21.3",
|
||||
"postcss": "^8.4.43",
|
||||
@@ -1520,6 +1522,7 @@
|
||||
"node_modules/vue": {
|
||||
"version": "3.5.27",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@vue/compiler-dom": "3.5.27",
|
||||
"@vue/compiler-sfc": "3.5.27",
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id" bordered stripe hover>
|
||||
<template #petId="{ row }">
|
||||
{{ getPetName(row.petId) }}
|
||||
</template>
|
||||
<template #statusSlot="{ row }">
|
||||
<span class="status-badge" :class="getStatusClass(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
@@ -33,6 +36,9 @@
|
||||
|
||||
<t-dialog v-model:visible="dialogVisible" header="新增预约" :on-confirm="submitCreate" width="500">
|
||||
<t-form :data="form" layout="vertical">
|
||||
<t-form-item v-if="!isCustomer" label="顾客" required>
|
||||
<t-select v-model="form.customerId" :options="customerOptions" placeholder="请选择顾客" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="宠物" required>
|
||||
<t-select v-model="form.petId" :options="petOptions" placeholder="请选择宠物" clearable />
|
||||
</t-form-item>
|
||||
@@ -40,7 +46,7 @@
|
||||
<t-date-picker v-model="form.appointmentDate" placeholder="请选择预约日期" />
|
||||
</t-form-item>
|
||||
<t-form-item label="时间段" required>
|
||||
<t-input v-model="form.timeSlot" placeholder="请输入时间段,如:09:00-10:00" />
|
||||
<t-select v-model="form.timeSlot" :options="timeSlotOptions" placeholder="请选择时间段" />
|
||||
</t-form-item>
|
||||
<t-form-item label="备注">
|
||||
<t-textarea v-model="form.remark" placeholder="请输入备注信息" :maxlength="200" />
|
||||
@@ -54,12 +60,17 @@
|
||||
import { onMounted, reactive, ref, computed } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const pets = ref([] as any[]);
|
||||
const customers = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const query = reactive({ status: '' });
|
||||
const form = reactive({ petId: '', appointmentDate: '', timeSlot: '' });
|
||||
const form = reactive({ customerId: '', petId: '', appointmentDate: '', timeSlot: '' });
|
||||
const auth = useAuthStore();
|
||||
|
||||
const isCustomer = computed(() => auth.user?.role === 'CUSTOMER');
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '待确认', value: 'PENDING' },
|
||||
@@ -69,8 +80,29 @@ const statusOptions = [
|
||||
{ label: '爽约', value: 'NO_SHOW' },
|
||||
];
|
||||
|
||||
const timeSlotOptions = [
|
||||
{ label: '09:00-10:00', value: '09:00-10:00' },
|
||||
{ label: '10:00-11:00', value: '10:00-11:00' },
|
||||
{ label: '11:00-12:00', value: '11:00-12:00' },
|
||||
{ label: '14:00-15:00', value: '14:00-15:00' },
|
||||
{ label: '15:00-16:00', value: '15:00-16:00' },
|
||||
{ label: '16:00-17:00', value: '16:00-17:00' },
|
||||
{ label: '17:00-18:00', value: '17:00-18:00' },
|
||||
];
|
||||
|
||||
const customerOptions = computed(() => {
|
||||
return customers.value.map((customer: any) => ({
|
||||
label: customer.username || `顾客${customer.id}`,
|
||||
value: customer.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const petOptions = computed(() => {
|
||||
return pets.value.map((pet: any) => ({
|
||||
const activeCustomerId = isCustomer.value ? auth.user?.userId : form.customerId;
|
||||
const filtered = activeCustomerId
|
||||
? pets.value.filter((pet: any) => pet.ownerId === activeCustomerId)
|
||||
: pets.value;
|
||||
return filtered.map((pet: any) => ({
|
||||
label: `${pet.name} (${pet.breed})`,
|
||||
value: pet.id
|
||||
}));
|
||||
@@ -87,6 +119,12 @@ const getStatusText = (status: string) => {
|
||||
return statusMap[status] || status;
|
||||
};
|
||||
|
||||
const getPetName = (petId: number) => {
|
||||
const pet = pets.value.find((item: any) => item.id === petId);
|
||||
if (!pet) return petId ? `宠物ID ${petId}` : '-';
|
||||
return `${pet.name}${pet.breed ? ` (${pet.breed})` : ''}`;
|
||||
};
|
||||
|
||||
const getStatusClass = (status: string) => {
|
||||
const classMap: Record<string, string> = {
|
||||
'PENDING': 'status-pending',
|
||||
@@ -100,7 +138,7 @@ const getStatusClass = (status: string) => {
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'petId', title: '宠物ID' },
|
||||
{ colKey: 'petId', title: '宠物' },
|
||||
{ colKey: 'appointmentDate', title: '预约日期' },
|
||||
{ colKey: 'timeSlot', title: '时段' },
|
||||
{ colKey: 'statusSlot', title: '状态' },
|
||||
@@ -132,6 +170,7 @@ const updateStatus = async (id: number, status: string) => {
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
form.customerId = isCustomer.value ? auth.user?.userId || '' : '';
|
||||
form.petId = '';
|
||||
form.appointmentDate = '';
|
||||
form.timeSlot = '';
|
||||
@@ -152,8 +191,26 @@ const loadPets = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
const loadCustomers = async () => {
|
||||
try {
|
||||
const res = await api.users({ page: 1, size: 100, role: 'CUSTOMER' });
|
||||
if (res.code === 0) {
|
||||
customers.value = res.data?.records || [];
|
||||
} else {
|
||||
MessagePlugin.error(res.message || '获取顾客列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取顾客列表失败:', error);
|
||||
MessagePlugin.error('获取顾客列表失败');
|
||||
}
|
||||
};
|
||||
|
||||
const submitCreate = async () => {
|
||||
const res = await api.createAppointment({ ...form });
|
||||
const payload = { ...form } as any;
|
||||
if (isCustomer.value) {
|
||||
payload.customerId = auth.user?.userId || payload.customerId;
|
||||
}
|
||||
const res = await api.createAppointment(payload);
|
||||
if (res.code === 0) {
|
||||
MessagePlugin.success('创建成功');
|
||||
dialogVisible.value = false;
|
||||
@@ -166,5 +223,8 @@ const submitCreate = async () => {
|
||||
onMounted(() => {
|
||||
load();
|
||||
loadPets(); // 加载宠物列表
|
||||
if (!isCustomer.value) {
|
||||
loadCustomers();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
<t-button variant="outline" @click="openCreate">新增病历</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #visitId="{ row }">
|
||||
{{ getVisitLabel(row.visitId) }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
{{ getStatusText(row.status) }}
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<div class="table-actions">
|
||||
<t-button size="small" variant="text" @click="openEdit(row)">编辑</t-button>
|
||||
@@ -21,7 +27,14 @@
|
||||
|
||||
<t-dialog v-model:visible="dialogVisible" :header="dialogTitle" :on-confirm="submit">
|
||||
<t-form :data="form">
|
||||
<t-form-item label="就诊ID"><t-input v-model="form.visitId" /></t-form-item>
|
||||
<t-form-item label="就诊ID">
|
||||
<t-select
|
||||
v-model="form.visitId"
|
||||
:options="visitOptions"
|
||||
placeholder="请选择就诊记录"
|
||||
clearable
|
||||
/>
|
||||
</t-form-item>
|
||||
<t-form-item label="主诉"><t-input v-model="form.chiefComplaint" /></t-form-item>
|
||||
<t-form-item label="诊断"><t-input v-model="form.diagnosis" /></t-form-item>
|
||||
<t-form-item label="状态"><t-select v-model="form.status" :options="statusOptions" /></t-form-item>
|
||||
@@ -31,11 +44,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref } from 'vue';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const visits = ref([] as any[]);
|
||||
const pets = ref([] as any[]);
|
||||
const doctors = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增病历');
|
||||
const editingId = ref<number | null>(null);
|
||||
@@ -47,9 +63,37 @@ const statusOptions = [
|
||||
{ label: '已完成', value: 'COMPLETED' },
|
||||
];
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
const map: Record<string, string> = {
|
||||
DRAFT: '草稿',
|
||||
COMPLETED: '已完成',
|
||||
};
|
||||
return map[status] || status || '-';
|
||||
};
|
||||
|
||||
const visitOptions = computed(() => {
|
||||
return visits.value.map((visit: any) => {
|
||||
const pet = pets.value.find((item: any) => item.id === visit.petId);
|
||||
const doctor = doctors.value.find((item: any) => item.id === visit.doctorId);
|
||||
const labelParts: string[] = [];
|
||||
if (pet) labelParts.push(`宠物 ${pet.name || '未命名'}`);
|
||||
if (doctor) labelParts.push(`医生 ${doctor.name || '未知'}`);
|
||||
return {
|
||||
label: labelParts.length ? labelParts.join(' / ') : '就诊记录',
|
||||
value: visit.id,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const getVisitLabel = (visitId: number) => {
|
||||
const visit = visits.value.find((item: any) => item.id === visitId);
|
||||
if (!visit) return visitId ? `就诊ID ${visitId}` : '-';
|
||||
return `就诊#${visit.id}`;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'visitId', title: '就诊ID' },
|
||||
{ colKey: 'visitId', title: '就诊' },
|
||||
{ colKey: 'chiefComplaint', title: '主诉' },
|
||||
{ colKey: 'diagnosis', title: '诊断' },
|
||||
{ colKey: 'status', title: '状态' },
|
||||
@@ -65,7 +109,7 @@ const load = async () => {
|
||||
const openCreate = () => {
|
||||
dialogTitle.value = '新增病历';
|
||||
editingId.value = null;
|
||||
form.visitId = query.visitId;
|
||||
form.visitId = query.visitId ? Number(query.visitId) : '';
|
||||
form.chiefComplaint = '';
|
||||
form.diagnosis = '';
|
||||
form.status = 'DRAFT';
|
||||
@@ -82,6 +126,23 @@ const openEdit = (row: any) => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
const loadVisits = async () => {
|
||||
const res = await api.visits({ page: 1, size: 100 });
|
||||
if (res.code === 0) {
|
||||
visits.value = res.data?.records || res.data || [];
|
||||
}
|
||||
};
|
||||
|
||||
const loadPets = async () => {
|
||||
const res = await api.pets({ page: 1, size: 200 });
|
||||
if (res.code === 0) pets.value = res.data?.records || [];
|
||||
};
|
||||
|
||||
const loadDoctors = async () => {
|
||||
const res = await api.users({ page: 1, size: 200, role: 'DOCTOR' });
|
||||
if (res.code === 0) doctors.value = res.data?.records || [];
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
const payload = { ...form };
|
||||
const res = editingId.value
|
||||
@@ -105,4 +166,10 @@ const remove = async (id: number) => {
|
||||
MessagePlugin.error(res.message || '删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadVisits();
|
||||
loadPets();
|
||||
loadDoctors();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
<t-button theme="primary" @click="load">查询</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #status="{ row }">
|
||||
{{ getStatusText(row.status) }}
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<t-button size="small" variant="text" @click="openReply(row)">回复</t-button>
|
||||
</template>
|
||||
@@ -39,6 +42,14 @@ const statusOptions = [
|
||||
{ label: '已处理', value: 'PROCESSED' },
|
||||
];
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
const map: Record<string, string> = {
|
||||
PENDING: '待处理',
|
||||
PROCESSED: '已处理',
|
||||
};
|
||||
return map[status] || status || '-';
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'title', title: '标题' },
|
||||
|
||||
@@ -7,12 +7,15 @@
|
||||
<template #icon><t-icon name="refresh" /></template>
|
||||
刷新
|
||||
</t-button>
|
||||
<t-button variant="outline" @click="openCreate">
|
||||
<t-button v-if="canEdit" variant="outline" @click="openCreate">
|
||||
<template #icon><t-icon name="add" /></template>
|
||||
新增订单
|
||||
</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id" bordered stripe hover>
|
||||
<template #visitId="{ row }">
|
||||
{{ getVisitLabel(row.visitId) }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<span class="status-badge" :class="getStatusClass(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
@@ -23,7 +26,7 @@
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<div class="table-actions">
|
||||
<t-button size="small" variant="outline" @click="openEdit(row)">
|
||||
<t-button v-if="canEdit" size="small" variant="outline" @click="openEdit(row)">
|
||||
<template #icon><t-icon name="edit" /></template>
|
||||
编辑
|
||||
</t-button>
|
||||
@@ -65,6 +68,7 @@
|
||||
import { onMounted, reactive, ref, computed } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const visits = ref([] as any[]);
|
||||
@@ -72,6 +76,9 @@ const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增订单');
|
||||
const editingId = ref<number | null>(null);
|
||||
const form = reactive({ visitId: '', amount: '', paymentMethod: 'OFFLINE', status: 'UNPAID' });
|
||||
const auth = useAuthStore();
|
||||
|
||||
const canEdit = computed(() => auth.user?.role === 'ADMIN');
|
||||
|
||||
const visitOptions = computed(() => {
|
||||
return visits.value.map((visit: any) => ({
|
||||
@@ -80,6 +87,12 @@ const visitOptions = computed(() => {
|
||||
}));
|
||||
});
|
||||
|
||||
const getVisitLabel = (visitId: number) => {
|
||||
const visit = visits.value.find((item: any) => item.id === visitId);
|
||||
if (!visit) return visitId ? `就诊ID ${visitId}` : '-';
|
||||
return `就诊#${visit.id}`;
|
||||
};
|
||||
|
||||
const getStatusText = (status: string) => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'UNPAID': '未支付',
|
||||
@@ -127,7 +140,7 @@ const statusOptions = [
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'visitId', title: '就诊ID' },
|
||||
{ colKey: 'visitId', title: '就诊' },
|
||||
{ colKey: 'amount', title: '金额' },
|
||||
{ colKey: 'status', title: '状态' },
|
||||
{ colKey: 'paymentMethod', title: '支付方式' },
|
||||
@@ -140,6 +153,7 @@ const load = async () => {
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
if (!canEdit.value) return;
|
||||
dialogTitle.value = '新增订单';
|
||||
editingId.value = null;
|
||||
form.visitId = '';
|
||||
@@ -150,6 +164,7 @@ const openCreate = () => {
|
||||
};
|
||||
|
||||
const openEdit = (row: any) => {
|
||||
if (!canEdit.value) return;
|
||||
dialogTitle.value = '编辑订单';
|
||||
editingId.value = row.id;
|
||||
form.visitId = row.visitId || '';
|
||||
@@ -174,6 +189,7 @@ const loadVisits = async () => {
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (!canEdit.value) return;
|
||||
const payload = { ...form };
|
||||
const res = editingId.value
|
||||
? await api.updateOrder(editingId.value, payload)
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
<h2 class="page-title">宠物档案</h2>
|
||||
<div class="panel">
|
||||
<div class="inline-form">
|
||||
<t-input v-model="query.ownerId" placeholder="主人ID" style="width: 160px" />
|
||||
<t-input v-if="!isCustomer" v-model="query.ownerId" placeholder="主人ID" style="width: 160px" />
|
||||
<t-button theme="primary" @click="load">查询</t-button>
|
||||
<t-button variant="outline" @click="openCreate">新增宠物</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #gender="{ row }">
|
||||
{{ getGenderText(row.gender) }}
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<div class="table-actions">
|
||||
<t-button size="small" variant="text" @click="openEdit(row)">编辑</t-button>
|
||||
@@ -21,6 +24,9 @@
|
||||
|
||||
<t-dialog v-model:visible="dialogVisible" :header="dialogTitle" :on-confirm="submit">
|
||||
<t-form :data="form">
|
||||
<t-form-item v-if="!isCustomer" label="主人" required>
|
||||
<t-select v-model="form.ownerId" :options="customerOptions" placeholder="请选择主人" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="名称"><t-input v-model="form.name" /></t-form-item>
|
||||
<t-form-item label="品种"><t-input v-model="form.breed" /></t-form-item>
|
||||
<t-form-item label="性别"><t-select v-model="form.gender" :options="genderOptions" /></t-form-item>
|
||||
@@ -31,16 +37,28 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
const query = reactive({ ownerId: '' });
|
||||
const list = ref([] as any[]);
|
||||
const customers = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增宠物');
|
||||
const editingId = ref<number | null>(null);
|
||||
const form = reactive({ name: '', breed: '', gender: 'MALE', weight: '' });
|
||||
const form = reactive({ ownerId: '', name: '', breed: '', gender: 'MALE', weight: '' });
|
||||
const auth = useAuthStore();
|
||||
|
||||
const isCustomer = computed(() => auth.user?.role === 'CUSTOMER');
|
||||
|
||||
const customerOptions = computed(() => {
|
||||
return customers.value.map((customer: any) => ({
|
||||
label: customer.username || `顾客${customer.id}`,
|
||||
value: customer.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
@@ -56,6 +74,14 @@ const genderOptions = [
|
||||
{ label: '雌性', value: 'FEMALE' },
|
||||
];
|
||||
|
||||
const getGenderText = (gender: string) => {
|
||||
const map: Record<string, string> = {
|
||||
MALE: '雄性',
|
||||
FEMALE: '雌性',
|
||||
};
|
||||
return map[gender] || gender || '-';
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
const params: any = { page: 1, size: 20 };
|
||||
if (query.ownerId) params.ownerId = query.ownerId;
|
||||
@@ -66,6 +92,9 @@ const load = async () => {
|
||||
const openCreate = () => {
|
||||
dialogTitle.value = '新增宠物';
|
||||
editingId.value = null;
|
||||
form.ownerId = isCustomer.value
|
||||
? auth.user?.userId || ''
|
||||
: query.ownerId ? Number(query.ownerId) : '';
|
||||
form.name = '';
|
||||
form.breed = '';
|
||||
form.gender = 'MALE';
|
||||
@@ -76,6 +105,7 @@ const openCreate = () => {
|
||||
const openEdit = (row: any) => {
|
||||
dialogTitle.value = '编辑宠物';
|
||||
editingId.value = row.id;
|
||||
form.ownerId = row.ownerId || '';
|
||||
form.name = row.name || '';
|
||||
form.breed = row.breed || '';
|
||||
form.gender = row.gender || 'MALE';
|
||||
@@ -83,8 +113,18 @@ const openEdit = (row: any) => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
const loadCustomers = async () => {
|
||||
const res = await api.users({ page: 1, size: 100, role: 'CUSTOMER' });
|
||||
if (res.code === 0) {
|
||||
customers.value = res.data?.records || [];
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
const payload = { ...form };
|
||||
const payload = { ...form } as any;
|
||||
if (isCustomer.value) {
|
||||
payload.ownerId = auth.user?.userId || payload.ownerId;
|
||||
}
|
||||
const res = editingId.value
|
||||
? await api.updatePet(editingId.value, payload)
|
||||
: await api.createPet(payload);
|
||||
@@ -107,5 +147,10 @@ const remove = async (id: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(load);
|
||||
onMounted(() => {
|
||||
load();
|
||||
if (!isCustomer.value) {
|
||||
loadCustomers();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id" bordered stripe hover>
|
||||
<template #visitId="{ row }">
|
||||
{{ getVisitLabel(row.visitId) }}
|
||||
</template>
|
||||
<template #doctorId="{ row }">
|
||||
{{ getDoctorName(row.doctorId) }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<span class="status-badge" :class="getStatusClass(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
@@ -71,6 +77,18 @@ const visitOptions = computed(() => {
|
||||
}));
|
||||
});
|
||||
|
||||
const getVisitLabel = (visitId: number) => {
|
||||
const visit = visits.value.find((item: any) => item.id === visitId);
|
||||
if (!visit) return visitId ? `就诊ID ${visitId}` : '-';
|
||||
return `就诊#${visit.id}`;
|
||||
};
|
||||
|
||||
const getDoctorName = (doctorId: number) => {
|
||||
const doctor = doctors.value.find((item: any) => item.id === doctorId);
|
||||
if (!doctor) return doctorId ? `医生ID ${doctorId}` : '-';
|
||||
return doctor.name || `医生${doctor.id}`;
|
||||
};
|
||||
|
||||
const doctorOptions = computed(() => {
|
||||
return doctors.value.map((doctor: any) => ({
|
||||
label: doctor.name,
|
||||
@@ -107,8 +125,8 @@ const statusOptions = [
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'visitId', title: '就诊ID' },
|
||||
{ colKey: 'doctorId', title: '医生ID' },
|
||||
{ colKey: 'visitId', title: '就诊' },
|
||||
{ colKey: 'doctorId', title: '医生' },
|
||||
{ colKey: 'status', title: '状态' },
|
||||
{ colKey: 'op', title: '操作', width: 100 },
|
||||
];
|
||||
|
||||
@@ -3,15 +3,18 @@
|
||||
<h2 class="page-title">检查报告</h2>
|
||||
<div class="panel">
|
||||
<div class="inline-form">
|
||||
<t-input v-model="query.petId" placeholder="宠物ID" style="width: 160px" />
|
||||
<t-select v-model="query.petId" :options="petOptions" placeholder="宠物" style="width: 200px" clearable />
|
||||
<t-button theme="primary" @click="load">查询</t-button>
|
||||
<t-button variant="outline" @click="openCreate">新增报告</t-button>
|
||||
<t-button v-if="canEdit" variant="outline" @click="openCreate">新增报告</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #petId="{ row }">
|
||||
{{ getPetName(row.petId) }}
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<div class="table-actions">
|
||||
<t-button size="small" variant="text" @click="openEdit(row)">编辑</t-button>
|
||||
<t-popconfirm content="确认删除?" @confirm="remove(row.id)">
|
||||
<t-button v-if="canEdit" size="small" variant="text" @click="openEdit(row)">编辑</t-button>
|
||||
<t-popconfirm v-if="canEdit" content="确认删除?" @confirm="remove(row.id)">
|
||||
<t-button size="small" theme="danger" variant="text">删除</t-button>
|
||||
</t-popconfirm>
|
||||
</div>
|
||||
@@ -21,8 +24,12 @@
|
||||
|
||||
<t-dialog v-model:visible="dialogVisible" :header="dialogTitle" :on-confirm="submit">
|
||||
<t-form :data="form">
|
||||
<t-form-item label="就诊ID"><t-input v-model="form.visitId" /></t-form-item>
|
||||
<t-form-item label="宠物ID"><t-input v-model="form.petId" /></t-form-item>
|
||||
<t-form-item label="就诊记录" required>
|
||||
<t-select v-model="form.visitId" :options="visitOptions" placeholder="请选择就诊记录" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="宠物" required>
|
||||
<t-select v-model="form.petId" :options="petOptions" placeholder="请选择宠物" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="类型"><t-input v-model="form.type" /></t-form-item>
|
||||
<t-form-item label="标题"><t-input v-model="form.title" /></t-form-item>
|
||||
<t-form-item label="摘要"><t-textarea v-model="form.summary" /></t-form-item>
|
||||
@@ -32,20 +39,51 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const visits = ref([] as any[]);
|
||||
const pets = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增报告');
|
||||
const editingId = ref<number | null>(null);
|
||||
const query = reactive({ petId: '' });
|
||||
const form = reactive({ visitId: '', petId: '', type: '', title: '', summary: '' });
|
||||
const auth = useAuthStore();
|
||||
|
||||
const canEdit = computed(() => ['ADMIN', 'DOCTOR'].includes(auth.user?.role || ''));
|
||||
|
||||
const visitOptions = computed(() => {
|
||||
return visits.value.map((visit: any) => {
|
||||
const labelParts = [`就诊ID ${visit.id}`];
|
||||
if (visit.petId) labelParts.push(`宠物ID ${visit.petId}`);
|
||||
if (visit.customerId) labelParts.push(`顾客ID ${visit.customerId}`);
|
||||
return {
|
||||
label: labelParts.join(' / '),
|
||||
value: visit.id,
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const petOptions = computed(() => {
|
||||
return pets.value.map((pet: any) => ({
|
||||
label: `${pet.name || '宠物'}${pet.breed ? ` (${pet.breed})` : ''}`,
|
||||
value: pet.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const getPetName = (petId: number) => {
|
||||
const pet = pets.value.find((item: any) => item.id === petId);
|
||||
if (!pet) return petId ? `宠物ID ${petId}` : '-';
|
||||
return `${pet.name || '宠物'}${pet.breed ? ` (${pet.breed})` : ''}`;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'petId', title: '宠物ID' },
|
||||
{ colKey: 'petId', title: '宠物' },
|
||||
{ colKey: 'type', title: '类型' },
|
||||
{ colKey: 'title', title: '标题' },
|
||||
{ colKey: 'op', title: '操作', width: 140 },
|
||||
@@ -59,10 +97,11 @@ const load = async () => {
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
if (!canEdit.value) return;
|
||||
dialogTitle.value = '新增报告';
|
||||
editingId.value = null;
|
||||
form.visitId = '';
|
||||
form.petId = query.petId;
|
||||
form.petId = query.petId ? Number(query.petId) : '';
|
||||
form.type = '';
|
||||
form.title = '';
|
||||
form.summary = '';
|
||||
@@ -70,6 +109,7 @@ const openCreate = () => {
|
||||
};
|
||||
|
||||
const openEdit = (row: any) => {
|
||||
if (!canEdit.value) return;
|
||||
dialogTitle.value = '编辑报告';
|
||||
editingId.value = row.id;
|
||||
form.visitId = row.visitId || '';
|
||||
@@ -80,7 +120,18 @@ const openEdit = (row: any) => {
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
const loadVisits = async () => {
|
||||
const res = await api.visits({ page: 1, size: 100 });
|
||||
if (res.code === 0) visits.value = res.data?.records || [];
|
||||
};
|
||||
|
||||
const loadPets = async () => {
|
||||
const res = await api.pets({ page: 1, size: 100 });
|
||||
if (res.code === 0) pets.value = res.data?.records || [];
|
||||
};
|
||||
|
||||
const submit = async () => {
|
||||
if (!canEdit.value) return;
|
||||
const payload = { ...form };
|
||||
const res = editingId.value
|
||||
? await api.updateReport(editingId.value, payload)
|
||||
@@ -95,6 +146,7 @@ const submit = async () => {
|
||||
};
|
||||
|
||||
const remove = async (id: number) => {
|
||||
if (!canEdit.value) return;
|
||||
const res = await api.deleteReport(id);
|
||||
if (res.code === 0) {
|
||||
MessagePlugin.success('删除成功');
|
||||
@@ -104,5 +156,9 @@ const remove = async (id: number) => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(load);
|
||||
onMounted(() => {
|
||||
load();
|
||||
loadVisits();
|
||||
loadPets();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -6,12 +6,18 @@
|
||||
<t-button theme="primary" @click="load">刷新</t-button>
|
||||
<t-button variant="outline" @click="openCreate">新增入库</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id" />
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #drugId="{ row }">
|
||||
{{ getDrugName(row.drugId) }}
|
||||
</template>
|
||||
</t-table>
|
||||
</div>
|
||||
|
||||
<t-dialog v-model:visible="dialogVisible" header="新增入库" :on-confirm="submitCreate">
|
||||
<t-form :data="form">
|
||||
<t-form-item label="药品ID"><t-input v-model="form.drugId" /></t-form-item>
|
||||
<t-form-item label="药品" required>
|
||||
<t-select v-model="form.drugId" :options="drugOptions" placeholder="请选择药品" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="数量"><t-input v-model="form.quantity" /></t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
@@ -19,17 +25,31 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const form = reactive({ drugId: '', quantity: '' });
|
||||
const drugs = ref([] as any[]);
|
||||
|
||||
const drugOptions = computed(() => {
|
||||
return drugs.value.map((drug: any) => ({
|
||||
label: drug.name,
|
||||
value: drug.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const getDrugName = (drugId: number) => {
|
||||
const drug = drugs.value.find((item: any) => item.id === drugId);
|
||||
if (!drug) return drugId ? `药品ID ${drugId}` : '-';
|
||||
return drug.name || `药品${drug.id}`;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'drugId', title: '药品ID' },
|
||||
{ colKey: 'drugId', title: '药品' },
|
||||
{ colKey: 'quantity', title: '数量' },
|
||||
{ colKey: 'stockInTime', title: '入库时间' },
|
||||
];
|
||||
@@ -39,6 +59,11 @@ const load = async () => {
|
||||
if (res.code === 0) list.value = res.data.records || [];
|
||||
};
|
||||
|
||||
const loadDrugs = async () => {
|
||||
const res = await api.drugs({ page: 1, size: 200 });
|
||||
if (res.code === 0) drugs.value = res.data?.records || [];
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
form.drugId = '';
|
||||
form.quantity = '';
|
||||
@@ -56,5 +81,8 @@ const submitCreate = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(load);
|
||||
onMounted(() => {
|
||||
load();
|
||||
loadDrugs();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -6,12 +6,18 @@
|
||||
<t-button theme="primary" @click="load">刷新</t-button>
|
||||
<t-button variant="outline" @click="openCreate">新增出库</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id" />
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #drugId="{ row }">
|
||||
{{ getDrugName(row.drugId) }}
|
||||
</template>
|
||||
</t-table>
|
||||
</div>
|
||||
|
||||
<t-dialog v-model:visible="dialogVisible" header="新增出库" :on-confirm="submitCreate">
|
||||
<t-form :data="form">
|
||||
<t-form-item label="药品ID"><t-input v-model="form.drugId" /></t-form-item>
|
||||
<t-form-item label="药品" required>
|
||||
<t-select v-model="form.drugId" :options="drugOptions" placeholder="请选择药品" clearable />
|
||||
</t-form-item>
|
||||
<t-form-item label="数量"><t-input v-model="form.quantity" /></t-form-item>
|
||||
</t-form>
|
||||
</t-dialog>
|
||||
@@ -19,17 +25,31 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const form = reactive({ drugId: '', quantity: '' });
|
||||
const drugs = ref([] as any[]);
|
||||
|
||||
const drugOptions = computed(() => {
|
||||
return drugs.value.map((drug: any) => ({
|
||||
label: drug.name,
|
||||
value: drug.id,
|
||||
}));
|
||||
});
|
||||
|
||||
const getDrugName = (drugId: number) => {
|
||||
const drug = drugs.value.find((item: any) => item.id === drugId);
|
||||
if (!drug) return drugId ? `药品ID ${drugId}` : '-';
|
||||
return drug.name || `药品${drug.id}`;
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'drugId', title: '药品ID' },
|
||||
{ colKey: 'drugId', title: '药品' },
|
||||
{ colKey: 'quantity', title: '数量' },
|
||||
{ colKey: 'stockOutTime', title: '出库时间' },
|
||||
];
|
||||
@@ -39,6 +59,11 @@ const load = async () => {
|
||||
if (res.code === 0) list.value = res.data.records || [];
|
||||
};
|
||||
|
||||
const loadDrugs = async () => {
|
||||
const res = await api.drugs({ page: 1, size: 200 });
|
||||
if (res.code === 0) drugs.value = res.data?.records || [];
|
||||
};
|
||||
|
||||
const openCreate = () => {
|
||||
form.drugId = '';
|
||||
form.quantity = '';
|
||||
@@ -56,5 +81,8 @@ const submitCreate = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(load);
|
||||
onMounted(() => {
|
||||
load();
|
||||
loadDrugs();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -8,6 +8,12 @@
|
||||
<t-button variant="outline" @click="openCreate">新增账号</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id">
|
||||
<template #role="{ row }">
|
||||
{{ getRoleText(row.role) }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
{{ getStatusText(row.status) }}
|
||||
</template>
|
||||
<template #op="{ row }">
|
||||
<div class="table-actions">
|
||||
<t-button size="small" variant="text" @click="toggleStatus(row)">
|
||||
@@ -54,6 +60,23 @@ const roleOptions = [
|
||||
{ label: '顾客', value: 'CUSTOMER' },
|
||||
];
|
||||
|
||||
const getRoleText = (role: string) => {
|
||||
const map: Record<string, string> = {
|
||||
ADMIN: '管理员',
|
||||
DOCTOR: '医生',
|
||||
CUSTOMER: '顾客',
|
||||
};
|
||||
return map[role] || role || '-';
|
||||
};
|
||||
|
||||
const getStatusText = (status: number) => {
|
||||
const map: Record<number, string> = {
|
||||
0: '禁用',
|
||||
1: '启用',
|
||||
};
|
||||
return map[status] ?? '-';
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'username', title: '用户名' },
|
||||
|
||||
@@ -13,6 +13,15 @@
|
||||
</t-button>
|
||||
</div>
|
||||
<t-table :data="list" :columns="columns" row-key="id" bordered stripe hover>
|
||||
<template #customerId="{ row }">
|
||||
{{ getCustomerName(row.customerId) }}
|
||||
</template>
|
||||
<template #petId="{ row }">
|
||||
{{ getPetName(row.petId) }}
|
||||
</template>
|
||||
<template #doctorId="{ row }">
|
||||
{{ getDoctorName(row.doctorId) }}
|
||||
</template>
|
||||
<template #status="{ row }">
|
||||
<span class="status-badge" :class="getStatusClass(row.status)">
|
||||
{{ getStatusText(row.status) }}
|
||||
@@ -90,6 +99,7 @@
|
||||
import { onMounted, reactive, ref, computed } from 'vue';
|
||||
import { MessagePlugin } from 'tdesign-vue-next';
|
||||
import { api } from '../api';
|
||||
import { useAuthStore } from '../store/auth';
|
||||
|
||||
const list = ref([] as any[]);
|
||||
const customers = ref([] as any[]);
|
||||
@@ -98,6 +108,9 @@ const doctors = ref([] as any[]);
|
||||
const dialogVisible = ref(false);
|
||||
const dialogTitle = ref('新增就诊');
|
||||
const editingId = ref<number | null>(null);
|
||||
const auth = useAuthStore();
|
||||
|
||||
const isAdmin = computed(() => auth.user?.role === 'ADMIN');
|
||||
const form = reactive({
|
||||
customerId: '',
|
||||
petId: '',
|
||||
@@ -123,7 +136,7 @@ const petOptions = computed(() => {
|
||||
|
||||
const doctorOptions = computed(() => {
|
||||
return doctors.value.map((doctor: any) => ({
|
||||
label: doctor.name,
|
||||
label: doctor.username || doctor.name,
|
||||
value: doctor.id
|
||||
}));
|
||||
});
|
||||
@@ -137,6 +150,24 @@ const getStatusText = (status: string) => {
|
||||
return statusMap[status] || status;
|
||||
};
|
||||
|
||||
const getCustomerName = (customerId: number) => {
|
||||
const customer = customers.value.find((item: any) => item.id === customerId);
|
||||
if (!customer) return customerId ? `顾客ID ${customerId}` : '-';
|
||||
return customer.username || `顾客${customer.id}`;
|
||||
};
|
||||
|
||||
const getPetName = (petId: number) => {
|
||||
const pet = pets.value.find((item: any) => item.id === petId);
|
||||
if (!pet) return petId ? `宠物ID ${petId}` : '-';
|
||||
return `${pet.name}${pet.breed ? ` (${pet.breed})` : ''}`;
|
||||
};
|
||||
|
||||
const getDoctorName = (doctorId: number) => {
|
||||
const doctor = doctors.value.find((item: any) => item.id === doctorId);
|
||||
if (!doctor) return doctorId ? `医生ID ${doctorId}` : '-';
|
||||
return doctor.username || doctor.name || `医生${doctor.id}`;
|
||||
};
|
||||
|
||||
const getStatusClass = (status: string) => {
|
||||
const classMap: Record<string, string> = {
|
||||
'IN_PROGRESS': 'status-pending',
|
||||
@@ -188,9 +219,9 @@ const payMethodOptions = [
|
||||
|
||||
const columns = [
|
||||
{ colKey: 'id', title: 'ID', width: 80 },
|
||||
{ colKey: 'customerId', title: '顾客ID' },
|
||||
{ colKey: 'petId', title: '宠物ID' },
|
||||
{ colKey: 'doctorId', title: '医生ID' },
|
||||
{ colKey: 'customerId', title: '顾客' },
|
||||
{ colKey: 'petId', title: '宠物' },
|
||||
{ colKey: 'doctorId', title: '医生' },
|
||||
{ colKey: 'status', title: '就诊状态' },
|
||||
{ colKey: 'paymentStatus', title: '支付状态' },
|
||||
{ colKey: 'totalAmount', title: '总金额', width: 100 },
|
||||
@@ -207,7 +238,7 @@ const openCreate = () => {
|
||||
editingId.value = null;
|
||||
form.customerId = '';
|
||||
form.petId = '';
|
||||
form.doctorId = '';
|
||||
form.doctorId = isAdmin.value ? '' : auth.user?.userId || '';
|
||||
form.status = 'IN_PROGRESS';
|
||||
form.paymentStatus = 'UNPAID';
|
||||
form.paymentMethod = 'OFFLINE';
|
||||
@@ -227,6 +258,10 @@ const openEdit = (row: any) => {
|
||||
};
|
||||
|
||||
const loadCustomers = async () => {
|
||||
if (!isAdmin.value) {
|
||||
customers.value = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api.users({ page: 1, size: 100, role: 'CUSTOMER' }); // 获取所有顾客
|
||||
if (res.code === 0) {
|
||||
@@ -255,6 +290,12 @@ const loadPets = async () => {
|
||||
};
|
||||
|
||||
const loadDoctors = async () => {
|
||||
if (!isAdmin.value) {
|
||||
doctors.value = auth.user?.userId
|
||||
? [{ id: auth.user.userId, username: auth.user.username }]
|
||||
: [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await api.users({ page: 1, size: 100, role: 'DOCTOR' }); // 获取所有医生
|
||||
if (res.code === 0) {
|
||||
|
||||
Reference in New Issue
Block a user