| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- <template>
- <div
- :class="['elderly-card-large', { active: isActive, flashing: elderly._flashEffect }]"
- @click="selectElderly"
- >
- <div v-if="hasWarning" class="warning-action-group">
- <Icon icon="mdi:alert" class="warning-flag" color="red" :size="50" />
- <el-button type="warning" size="small" class="handle-warning-btn" @click.stop="handleWarning">
- 去处理
- </el-button>
- </div>
- <div class="elderly-avatar-large" :class="getGenderClass(elderly.gender)">
- <span class="avatar-initial">{{ getNameInitial(elderly.name) }}</span>
- </div>
- <div class="elderly-info-large">
- <h3>{{ elderly.name }}</h3>
- <p>{{ elderly.age || 0 }}岁 • {{ genderMap[elderly.gender] || '未知' }}</p>
- <div class="health-status-large">
- <div class="status-dot" :class="getHealthStatusClass(elderly.healthText)"></div>
- <span>{{ elderly.healthText || '未知' }}</span>
- </div>
- <div v-if="elderly.address" class="address-row" :title="elderly.address">
- <Icon icon="mdi:map-marker" :size="16" />
- <span class="address-text">{{ elderly.address }}</span>
- </div>
- </div>
- <div class="elderly-actions">
- <div class="device-count">
- <span>{{ elderly.deviceNumber || 0 }} 个设备</span>
- </div>
- <el-button
- type="primary"
- size="small"
- class="add-device-btn"
- style="padding: 10px !important"
- @click.stop="addDevice"
- >
- 添加设备
- </el-button>
- </div>
- </div>
- </template>
- <script lang="ts" setup>
- interface Elderly {
- id: number
- avatar: string
- name: string
- age: number
- gender: string
- healthStatus: string
- healthText: string
- address?: string
- deviceNumber: number
- _flashEffect?: boolean
- }
- interface Props {
- elderly: Elderly
- isActive: boolean
- hasWarning: boolean
- }
- const props = defineProps<Props>()
- const emit = defineEmits<{
- select: [elderly: Elderly]
- addDevice: [elderly: Elderly]
- handleWarning: [elderly: Elderly]
- }>()
- const genderMap = {
- 0: '女',
- 1: '男'
- }
- const getGenderClass = (gender: string | number) => {
- const maleVals = [1, '1', '男', 'male', 'M', 'm']
- const femaleVals = [0, '0', '女', 'female', 'F', 'f']
- if (maleVals.includes(gender as any)) return 'male'
- if (femaleVals.includes(gender as any)) return 'female'
- return 'unknown'
- }
- const getNameInitial = (name?: string) => {
- if (!name) return '?'
- const s = String(name).trim()
- return s ? s[0] : '?'
- }
- const getHealthStatusClass = (healthText: string) => {
- if (!healthText) return ''
- if (healthText.includes('良好') || healthText.includes('稳定')) return 'good'
- if (healthText.includes('偏高') || healthText.includes('关注') || healthText.includes('严重'))
- return 'warning'
- if (healthText.includes('危险')) return 'error'
- return 'normal'
- }
- const selectElderly = () => {
- emit('select', props.elderly)
- }
- const addDevice = () => {
- emit('addDevice', props.elderly)
- }
- const handleWarning = () => {
- emit('handleWarning', props.elderly)
- }
- </script>
- <style lang="scss" scoped>
- $primary-color: #1a73e8;
- $text-light: #fff;
- $text-gray: #8a8f98;
- $success-color: #26de81;
- $warning-color: #fd9644;
- $danger-color: #ff6b6b;
- @keyframes borderFlash {
- 0%,
- 100% {
- border-color: rgb(255 255 255 / 8%);
- box-shadow: 0 0 0 0 rgb(255 107 107 / 0%);
- }
- 50% {
- border-color: $danger-color;
- box-shadow: 0 0 20px 5px rgb(255 107 107 / 50%);
- }
- }
- .elderly-card-large {
- position: relative;
- display: flex;
- padding: 20px;
- cursor: pointer;
- background: rgb(255 255 255 / 5%);
- border: 1px solid rgb(255 255 255 / 8%);
- border-radius: 12px;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
- align-items: center;
- gap: 15px;
- &:hover,
- &.active {
- background: rgb(26 115 232 / 20%);
- border-color: rgb(26 115 232 / 50%);
- transform: translateX(5px);
- }
- &.flashing {
- position: relative;
- z-index: 1;
- animation: borderFlash 1s ease-in-out infinite;
- }
- }
- .elderly-avatar-large {
- display: flex;
- width: 60px;
- height: 60px;
- background: rgb(255 255 255 / 10%);
- border-radius: 50%;
- align-items: center;
- justify-content: center;
- color: #fff;
- user-select: none;
- flex-shrink: 0;
- :deep(svg) {
- width: 32px !important;
- height: 32px !important;
- }
- &.male {
- background: #409eff;
- }
- &.female {
- background: #ff69b4;
- }
- &.unknown {
- background: #909399;
- }
- .avatar-initial {
- font-size: 22px;
- font-weight: 700;
- color: #fff;
- width: auto !important;
- height: auto !important;
- line-height: 1;
- }
- }
- .elderly-info-large {
- flex: 1;
- h3 {
- margin-bottom: 5px;
- font-size: 20px;
- }
- p {
- margin-bottom: 8px;
- color: $text-gray;
- }
- }
- .health-status-large {
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .address-row {
- display: flex;
- align-items: center;
- gap: 6px;
- color: $text-gray;
- margin-top: 6px;
- }
- .address-text {
- flex: 1;
- min-width: 0;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .status-dot {
- width: 12px;
- height: 12px;
- border-radius: 50%;
- box-shadow: 0 0 10px currentcolor;
- &.good {
- color: $success-color;
- background: $success-color;
- }
- &.warning {
- color: $warning-color;
- background: $warning-color;
- }
- &.error {
- color: $danger-color;
- background: $danger-color;
- }
- &.normal {
- color: $primary-color;
- background: $primary-color;
- }
- }
- .elderly-actions {
- display: flex;
- flex-direction: column;
- align-items: flex-end;
- gap: 8px;
- }
- .device-count {
- padding: 5px 10px;
- font-size: 14px;
- background: rgb(255 255 255 / 10%);
- border-radius: 20px;
- }
- .add-device-btn {
- white-space: nowrap;
- }
- .warning-action-group {
- position: absolute;
- top: 5px;
- left: 160px;
- z-index: 2;
- display: flex;
- align-items: center;
- gap: 10px;
- }
- .handle-warning-btn {
- white-space: nowrap;
- font-size: 14px;
- padding: 8px 16px !important;
- }
- </style>
|