index.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <template>
  2. <div class="page-container">
  3. <el-card shadow="never">
  4. <template #header>
  5. <div class="card-header">
  6. <span class="title">宠物档案</span>
  7. <div class="header-actions">
  8. <el-input v-model="searchKey" placeholder="搜索宠物名/主人" style="width: 200px; margin-right: 10px" clearable
  9. @keyup.enter="handleSearch" @clear="handleSearch" />
  10. <el-button type="primary" icon="Plus" @click="handleAdd" v-hasPermi="['archieves:pet:add']">新增档案</el-button>
  11. </div>
  12. </div>
  13. </template>
  14. <el-table :data="tableData" v-loading="loading" style="width: 100%">
  15. <el-table-column label="宠物信息" width="220">
  16. <template #default="scope">
  17. <div style="display: flex; align-items: center">
  18. <el-avatar :size="50" :src="scope.row.avatarUrl" style="margin-right: 10px" />
  19. <div>
  20. <div style="font-weight: bold">{{ scope.row.name }}</div>
  21. <div style="font-size: 12px; color: #999">{{ scope.row.breed }} | {{ scope.row.age }}岁</div>
  22. </div>
  23. </div>
  24. </template>
  25. </el-table-column>
  26. <el-table-column prop="gender" label="性别" width="80" align="center">
  27. <template #default="scope">
  28. <dict-tag :options="sys_pet_gender" :value="scope.row.gender" />
  29. </template>
  30. </el-table-column>
  31. <el-table-column label="所属主人" width="180">
  32. <template #default="scope">
  33. <div>{{ scope.row.ownerName }}</div>
  34. <div style="font-size: 12px; color: #666">{{ scope.row.ownerPhone }}</div>
  35. </template>
  36. </el-table-column>
  37. <el-table-column label="标签" min-width="150">
  38. <template #default="scope">
  39. <el-tag v-for="tag in scope.row.tags" :key="tag.id" :type="tag.colorType || 'info'" effect="light"
  40. size="small" style="margin-right: 5px">{{
  41. tag.name
  42. }}</el-tag>
  43. </template>
  44. </el-table-column>
  45. <el-table-column label="健康状态" width="100" align="center">
  46. <template #default="scope">
  47. <el-tag :type="scope.row.healthStatus === '健康' ? 'success' : 'warning'" effect="dark" size="small">{{
  48. scope.row.healthStatus }}</el-tag>
  49. </template>
  50. </el-table-column>
  51. <el-table-column label="疫苗接种" width="120" align="center">
  52. <template #default="scope">
  53. {{ scope.row.vaccineStatus || '-' }}
  54. </template>
  55. </el-table-column>
  56. <el-table-column label="操作" width="200" align="center">
  57. <template #default="scope">
  58. <el-button link type="primary" @click="handleDetail(scope.row)"
  59. v-hasPermi="['archieves:pet:query']">详情</el-button>
  60. <el-button link type="primary" @click="handleEdit(scope.row)"
  61. v-hasPermi="['archieves:pet:edit']">编辑</el-button>
  62. <el-button link type="primary" @click="handleRemark(scope.row)"
  63. v-hasPermi="['archieves:pet:remark']">备注</el-button>
  64. <el-button link type="danger" @click="handleDelete(scope.row)"
  65. v-hasPermi="['archieves:pet:remove']">删除</el-button>
  66. </template>
  67. </el-table-column>
  68. </el-table>
  69. <div class="pagination-container">
  70. <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize"
  71. :page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="total"
  72. @size-change="getList" @current-change="getList" />
  73. </div>
  74. </el-card>
  75. <AddPetDialog v-model:visible="dialogVisible" :user-options="userList" :pet-data="petEditData"
  76. @success="onPetSaved" />
  77. <!-- Pet Profile Drawer -->
  78. <PetDetailDrawer v-model:visible="drawerVisible" :pet-id="currentPet.id" :service-list="serviceList" editable
  79. @remark-saved="getList" />
  80. </div>
  81. </template>
  82. <script setup>
  83. import { ref, reactive, onMounted, getCurrentInstance, toRefs } from 'vue';
  84. import { ElMessage, ElMessageBox } from 'element-plus';
  85. import { listPet, getPet, delPet } from '@/api/archieves/pet'
  86. import { listAllTag } from '@/api/archieves/tag'
  87. import { listAllCustomer } from '@/api/archieves/customer'
  88. import { listAllService } from '@/api/service/list/index'
  89. import PetDetailDrawer from '@/components/PetDetailDrawer/index.vue'
  90. import AddPetDialog from '@/components/AddPetDialog/index.vue'
  91. const { proxy } = getCurrentInstance();
  92. const { sys_pet_gender, sys_pet_type, sys_pet_size } = toRefs(
  93. proxy?.useDict('sys_pet_gender', 'sys_pet_type', 'sys_pet_size')
  94. );
  95. const loading = ref(false);
  96. const total = ref(0);
  97. const tableData = ref([]);
  98. const queryParams = reactive({
  99. pageNum: 1,
  100. pageSize: 10,
  101. keyword: ''
  102. });
  103. const searchKey = ref('');
  104. const dialogVisible = ref(false);
  105. const drawerVisible = ref(false);
  106. const currentPet = ref({});
  107. const allPetTags = ref([]);
  108. const userList = ref([]);
  109. const petEditData = ref(null);
  110. const serviceList = ref([]);
  111. const getServiceList = () => {
  112. listAllService().then((res) => {
  113. serviceList.value = res.data || []
  114. })
  115. }
  116. const getList = () => {
  117. loading.value = true;
  118. queryParams.keyword = searchKey.value;
  119. listPet(queryParams).then((res) => {
  120. tableData.value = res.rows;
  121. total.value = res.total;
  122. }).finally(() => {
  123. loading.value = false;
  124. });
  125. };
  126. const handleSearch = () => {
  127. queryParams.pageNum = 1;
  128. getList();
  129. };
  130. const loadTags = () => {
  131. listAllTag({ category: 'pet', status: 0 }).then((res) => {
  132. allPetTags.value = res.data || [];
  133. });
  134. };
  135. const loadUsers = () => {
  136. listAllCustomer({ status: 0 }).then((res) => {
  137. userList.value = res.data || [];
  138. });
  139. };
  140. const handleAdd = () => {
  141. petEditData.value = null
  142. dialogVisible.value = true
  143. }
  144. const handleEdit = (row) => {
  145. getPet(row.id).then((res) => {
  146. petEditData.value = res.data
  147. dialogVisible.value = true
  148. })
  149. }
  150. const onPetSaved = () => {
  151. dialogVisible.value = false
  152. getList()
  153. }
  154. const handleDetail = (row) => {
  155. currentPet.value = row
  156. drawerVisible.value = true
  157. }
  158. const handleRemark = (row) => {
  159. currentPet.value = row
  160. drawerVisible.value = true
  161. // 由于备注功能已集成在详情抽屉中,直接打开抽屉即可,后期可以根据需要调整是否直接弹出备注对话框
  162. }
  163. const handleDelete = (row) => {
  164. ElMessageBox.confirm('确认删除该宠物档案吗?', '提示', { type: 'warning' }).then(() => {
  165. delPet(row.id).then(() => {
  166. ElMessage.success('删除成功');
  167. getList();
  168. });
  169. });
  170. };
  171. onMounted(() => {
  172. getList();
  173. loadTags();
  174. loadUsers();
  175. getServiceList();
  176. });
  177. </script>
  178. <style scoped>
  179. .page-container {
  180. padding: 20px;
  181. }
  182. .card-header {
  183. display: flex;
  184. justify-content: space-between;
  185. align-items: center;
  186. }
  187. .title {
  188. font-weight: bold;
  189. }
  190. .avatar-uploader-icon {
  191. font-size: 28px;
  192. color: #8c939d;
  193. width: 80px;
  194. height: 80px;
  195. text-align: center;
  196. border: 1px dashed #dcdfe6;
  197. border-radius: 50%;
  198. display: flex;
  199. justify-content: center;
  200. align-items: center;
  201. }
  202. .avatar-uploader-icon:hover {
  203. border-color: var(--el-color-primary);
  204. }
  205. .profile-header {
  206. display: flex;
  207. align-items: center;
  208. margin-bottom: 20px;
  209. padding-bottom: 20px;
  210. border-bottom: 1px solid #f0f0f0;
  211. }
  212. .profile-basic {
  213. margin-left: 20px;
  214. }
  215. .name-row {
  216. display: flex;
  217. align-items: center;
  218. }
  219. .name {
  220. font-size: 20px;
  221. font-weight: bold;
  222. color: #303133;
  223. }
  224. .section-title {
  225. font-size: 16px;
  226. font-weight: bold;
  227. margin-bottom: 15px;
  228. border-left: 4px solid #409eff;
  229. padding-left: 10px;
  230. line-height: 1.2;
  231. }
  232. .pagination-container {
  233. margin-top: 20px;
  234. display: flex;
  235. justify-content: flex-end;
  236. }
  237. </style>