CareSummaryDrawer.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. <template>
  2. <el-drawer
  3. v-model="drawerVisible"
  4. title="宠物护理工作小结"
  5. direction="rtl"
  6. size="750px"
  7. destroy-on-close
  8. class="care-summary-drawer"
  9. >
  10. <div class="care-summary-container" v-if="order">
  11. <!-- Pet Header -->
  12. <div class="summary-header">
  13. <div class="avatar-wrapper">
  14. <el-avatar :size="80" :src="order.petAvatar" shape="circle" class="pet-summary-avatar">{{ order.petName?.charAt(0) }}</el-avatar>
  15. </div>
  16. <div class="pet-summary-info">
  17. <div class="summary-name-row">
  18. <span class="name">{{ order.petName }}</span>
  19. <div class="tags-group">
  20. <el-tag :type="order.petGender==='male'?'':'danger'" effect="light" round>
  21. <el-icon><component :is="order.petGender==='male'?'Male':'Female'" /></el-icon>
  22. {{ order.petAge }}
  23. </el-tag>
  24. <el-tag v-for="tag in (order.petTags||[])" :key="tag" type="warning" effect="plain" round>{{ formatTag(tag) }}</el-tag>
  25. </div>
  26. </div>
  27. <div class="summary-sub-row">
  28. <div class="info-item">
  29. <span class="lbl">品种</span>
  30. <span class="val">{{ order.petBreed || '未知' }}</span>
  31. </div>
  32. <div class="divider-v"></div>
  33. <div class="info-item">
  34. <span class="lbl">体重</span>
  35. <span class="val">{{ order.petWeight }}</span>
  36. </div>
  37. <div class="divider-v"></div>
  38. <div class="info-item">
  39. <span class="lbl">主人</span>
  40. <span class="val">{{ order.userName || '未知' }}</span>
  41. </div>
  42. </div>
  43. </div>
  44. </div>
  45. <!-- Info Groups -->
  46. <div class="summary-section">
  47. <div class="sec-title">
  48. <span class="icon-box"><el-icon><List /></el-icon></span>
  49. 基本信息
  50. </div>
  51. <el-descriptions :column="2" border class="spacious-desc">
  52. <el-descriptions-item label="性格关键词">{{ order.petPersonality }}</el-descriptions-item>
  53. <el-descriptions-item label="健康状况">
  54. <el-tag :type="order.healthStatus==='健康'?'success':'danger'" effect="light" size="small">{{ order.healthStatus }}</el-tag>
  55. </el-descriptions-item>
  56. <el-descriptions-item label="疫苗情况">
  57. <div class="flex-align">
  58. <span style="color:#67c23a; margin-right:8px;" v-if="order.vaccineImg"><el-icon><CircleCheckFilled /></el-icon> 已接种</span>
  59. <span v-else style="color:#909399;">未接种</span>
  60. <el-image
  61. v-if="order.vaccineImg"
  62. style="width: 24px; height: 24px; border-radius:4px; vertical-align:middle; cursor:zoom-in;"
  63. :src="order.vaccineImg"
  64. :preview-src-list="[order.vaccineImg]"
  65. :preview-teleported="true"
  66. />
  67. </div>
  68. </el-descriptions-item>
  69. <el-descriptions-item label="过敏史">
  70. <span :style="{color: order.allergy ? '#f56c6c' : 'inherit'}">{{ order.allergy || '无' }}</span>
  71. </el-descriptions-item>
  72. </el-descriptions>
  73. </div>
  74. <div class="summary-section">
  75. <div class="sec-title">
  76. <span class="icon-box text-blue"><el-icon><HomeFilled /></el-icon></span>
  77. 服务环境
  78. </div>
  79. <el-descriptions :column="2" border class="spacious-desc">
  80. <el-descriptions-item label="到家时间">{{ order.homeTime }}</el-descriptions-item>
  81. <el-descriptions-item label="房屋类型">{{ order.houseType }}</el-descriptions-item>
  82. <el-descriptions-item label="入户方式" :span="2">
  83. <span style="font-weight:bold;">{{ order.entryMethod }}</span>
  84. <span style="margin-left:8px; color:#909399;">({{ order.entryDetail }})</span>
  85. </el-descriptions-item>
  86. </el-descriptions>
  87. </div>
  88. <!-- Service Log -->
  89. <div class="summary-section main-log">
  90. <div class="sec-title" style="border:none; padding-left:0; margin-bottom:16px;">
  91. <div class="left">
  92. <span class="icon-box text-orange"><el-icon><Notebook /></el-icon></span>
  93. 服务内容记录
  94. </div>
  95. <el-button v-if="!isEditingSummary" type="primary" link icon="Edit" @click="isEditingSummary = true">编辑</el-button>
  96. </div>
  97. <div v-if="isEditingSummary" class="edit-area">
  98. <el-input
  99. v-model="careSummaryText"
  100. type="textarea"
  101. :rows="12"
  102. placeholder="请输入详细的护理服务小结..."
  103. resize="none"
  104. />
  105. <div class="edit-actions">
  106. <el-button @click="isEditingSummary = false">取消</el-button>
  107. <el-button type="primary" @click="saveCareSummary">保存内容</el-button>
  108. </div>
  109. </div>
  110. <div v-else class="log-content-box">
  111. <pre class="log-text">{{ careSummaryText }}</pre>
  112. </div>
  113. </div>
  114. <!-- Footer Info -->
  115. <div class="summary-footer">
  116. <div class="footer-info">
  117. <div class="f-row">
  118. <span class="lbl">护宠师</span>
  119. <span class="val user-active">{{ order.fulfillerName || '当前履约者' }}</span>
  120. </div>
  121. <div class="f-row">
  122. <span class="lbl">提交时间</span>
  123. <span class="val">{{ order.summaryTime || '2024-02-04 17:00' }}</span>
  124. </div>
  125. </div>
  126. <div class="footer-action">
  127. <el-button size="large" @click="drawerVisible = false">关闭</el-button>
  128. </div>
  129. </div>
  130. </div>
  131. </el-drawer>
  132. </template>
  133. <script setup>
  134. import { ref, computed, watch } from 'vue'
  135. const props = defineProps({
  136. visible: Boolean,
  137. order: Object
  138. })
  139. const emit = defineEmits(['update:visible', 'submit'])
  140. const drawerVisible = computed({
  141. get: () => props.visible,
  142. set: (val) => emit('update:visible', val)
  143. })
  144. const careSummaryText = ref('')
  145. const isEditingSummary = ref(false)
  146. watch(() => props.visible, (val) => {
  147. if (val && props.order) {
  148. isEditingSummary.value = false
  149. if (!props.order.careSummary) {
  150. careSummaryText.value = `1. 精神/身体状态:${props.order.petName}精神状态良好,愿意互动。
  151. 2. 进食/饮水:食欲正常,饮水适当,已清洗碗具。
  152. 3. 排泄情况:排便正常,颜色形状正常,已清理。
  153. 4. 卫生情况:猫砂盆/地面已清理干净,无异味。
  154. 5. 互动情况:陪玩了20分钟,${props.order.petName}很开心。
  155. 6. 特殊情况/备注:无特殊异常。`
  156. } else {
  157. careSummaryText.value = props.order.careSummary
  158. }
  159. }
  160. })
  161. const saveCareSummary = () => {
  162. emit('submit', careSummaryText.value)
  163. isEditingSummary.value = false
  164. }
  165. const formatTag = (tag) => {
  166. if (!tag) return '';
  167. if (typeof tag === 'string') {
  168. try {
  169. const obj = JSON.parse(tag);
  170. return typeof obj === 'object' ? (obj.name || tag) : obj;
  171. } catch (e) {
  172. return tag;
  173. }
  174. }
  175. return tag.name || tag;
  176. }
  177. </script>
  178. <style scoped>
  179. /* Enhanced Care Summary Styles */
  180. .care-summary-drawer :deep(.el-drawer__header) { margin-bottom: 0; padding: 20px 24px; border-bottom: 1px solid #f0f0f0; }
  181. .care-summary-drawer :deep(.el-drawer__body) { padding: 0; overflow-y: auto; background: #fff; }
  182. .care-summary-container { padding: 32px 40px; }
  183. /* 1. Header */
  184. .summary-header { display: flex; gap: 24px; align-items: flex-start; margin-bottom: 32px; padding-bottom: 24px; border-bottom: 1px dashed #e4e7ed; }
  185. .avatar-wrapper { border: 4px solid #f2f6fc; border-radius: 50%; }
  186. .pet-summary-info { flex: 1; display:flex; flex-direction:column; gap:12px; padding-top: 4px; }
  187. .summary-name-row { display: flex; align-items: center; gap: 16px; margin-bottom: 4px; }
  188. .summary-name-row .name { font-size: 24px; font-weight: 800; color: #303133; letter-spacing: 0.5px; }
  189. .tags-group { display: flex; gap: 8px; align-items: center; }
  190. .summary-sub-row { display: flex; align-items: center; background: #f9fafe; padding: 10px 16px; border-radius: 8px; align-self: flex-start; }
  191. .info-item { display: flex; flex-direction: column; gap: 2px; }
  192. .info-item .lbl { font-size: 11px; color: #909399; text-transform: uppercase; }
  193. .info-item .val { font-size: 14px; font-weight: bold; color: #606266; }
  194. .divider-v { width: 1px; height: 24px; background: #ebeef5; margin: 0 16px; }
  195. /* 2. Sections */
  196. .summary-section { margin-bottom: 40px; }
  197. .sec-title {
  198. font-size: 16px; font-weight: 700; color: #303133; margin-bottom: 16px;
  199. display:flex; align-items:center; gap:8px;
  200. justify-content: space-between;
  201. }
  202. .sec-title .left { display: flex; align-items: center; gap: 8px; }
  203. .icon-box {
  204. width: 28px; height: 28px; background: #ecf5ff; color: #409eff; border-radius: 6px;
  205. display: flex; align-items: center; justify-content: center; font-size: 16px;
  206. }
  207. .icon-box.text-blue { background: #ecf5ff; color: #409eff; }
  208. .icon-box.text-orange { background: #fdf6ec; color: #e6a23c; }
  209. /* 3. Descriptions */
  210. .spacious-desc :deep(.el-descriptions__cell) { padding: 12px 16px!important; }
  211. .spacious-desc :deep(.el-descriptions__label) { width: 100px; color: #606266; font-weight: 500; background: #fafafa; }
  212. .flex-align { display: flex; align-items: center; }
  213. /* 4. Log Area */
  214. .main-log { background: #fff; }
  215. .log-content-box {
  216. background: #fff;
  217. border: 1px solid #ebeef5; border-radius: 8px;
  218. padding: 24px;
  219. box-shadow: 0 2px 12px rgba(0,0,0,0.02);
  220. position: relative;
  221. }
  222. .log-content-box::before {
  223. content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; background: #e6a23c; border-top-left-radius: 8px; border-bottom-left-radius: 8px;
  224. }
  225. .log-text {
  226. white-space: pre-wrap; font-family: 'Inter', system-ui, sans-serif; margin: 0; line-height: 1.8; font-size: 15px; color: #303133; text-align: justify;
  227. }
  228. .edit-actions { display: flex; justify-content: flex-end; gap: 12px; margin-top: 12px; }
  229. /* 5. Footer */
  230. .summary-footer {
  231. margin-top: 60px; padding-top: 24px; border-top: 1px solid #ebeef5;
  232. display: flex; justify-content: space-between; align-items: center;
  233. }
  234. .footer-info { display: flex; gap: 32px; }
  235. .f-row { display: flex; flex-direction: column; gap: 4px; }
  236. .f-row .lbl { font-size: 12px; color: #909399; }
  237. .f-row .val { font-size: 15px; font-weight: 600; color: #303133; }
  238. .f-row .val.user-active { color: #409eff; }
  239. </style>