This commit is contained in:
王子琦
2026-02-09 21:14:30 +08:00
parent 09028d97f7
commit f9bfb8556b
54 changed files with 6963 additions and 461 deletions

View File

@@ -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",

View File

@@ -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>

View File

@@ -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>

View File

@@ -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: '标题' },

View File

@@ -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)

View File

@@ -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>

View File

@@ -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 },
];

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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: '用户名' },

View File

@@ -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) {