| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <template>
- <div class="live-board-wrapper" :class="{ selected }" :style="{
- width: width + 'px',
- height: height + 'px',
- position: 'relative',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- background: '#f8fafc',
- boxSizing: 'border-box',
- border: selected ? '2px solid #409eff' : '1px solid #ddd',
- borderRadius: props.borderRadius + 'px'
- }">
- <div class="live-board-content">
- <div class="live-icon">
- <svg width="32" height="32" viewBox="0 0 32 32" fill="none">
- <rect x="3" y="3" width="26" height="26" rx="6" fill="#f3f6fa" stroke="#e67e22" stroke-width="2" />
- <path d="M12 11v10l8-5-8-5z" fill="#e67e22" />
- </svg>
- </div>
- <div class="live-info" v-if="liveUrl || selected">
- <div class="live-url" :title="liveUrl"><span class="label">直播地址:</span>{{ liveUrl || '未设置' }}</div>
- <div class="live-audio"><span class="label">音频:</span>{{ playAudio ? '开' : '关' }}</div>
- </div>
- </div>
- <template v-if="selected">
- <div v-for="dir in ['nw', 'ne', 'sw', 'se']" :key="dir" class="resize-handle" :class="'resize-' + dir"
- @mousedown.stop="onResizeMouseDown(dir, $event)" />
- </template>
- </div>
- </template>
- <script setup lang="ts">
- import { computed, withDefaults, defineEmits } from 'vue';
- const emit = defineEmits(['resize']);
- interface Props {
- width?: number;
- height?: number;
- liveUrl?: string;
- playAudio?: boolean;
- selected?: boolean;
- borderRadius?: number;
- }
- const props = withDefaults(defineProps<Props>(), {
- width: 200,
- height: 120,
- liveUrl: '',
- playAudio: true,
- selected: false,
- borderRadius: 0
- });
- const selected = computed(() => !!props.selected);
- const width = computed(() => props.width);
- const height = computed(() => props.height);
- const liveUrl = computed(() => props.liveUrl);
- const playAudio = computed(() => props.playAudio);
- let startX = 0,
- startY = 0,
- startW = 0,
- startH = 0;
- function onResizeMouseDown(dir: string, e: MouseEvent) {
- e.stopPropagation();
- startX = e.clientX;
- startY = e.clientY;
- startW = props.width;
- startH = props.height;
- function onMouseMove(ev: MouseEvent) {
- let dx = ev.clientX - startX;
- let dy = ev.clientY - startY;
- let newW = startW;
- let newH = startH;
- if (dir.includes('e')) newW = Math.max(40, startW + dx);
- if (dir.includes('s')) newH = Math.max(40, startH + dy);
- if (dir.includes('w')) newW = Math.max(40, startW - dx);
- if (dir.includes('n')) newH = Math.max(40, startH - dy);
- emit('resize', { width: newW, height: newH });
- }
- function onMouseUp() {
- window.removeEventListener('mousemove', onMouseMove);
- window.removeEventListener('mouseup', onMouseUp);
- }
- window.addEventListener('mousemove', onMouseMove);
- window.addEventListener('mouseup', onMouseUp);
- }
- function select() {
- // 交由父组件处理选中
- }
- </script>
- <style scoped>
- .live-board-wrapper {
- user-select: none;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- background: #f8fafc;
- transition: all 0.2s;
- position: relative;
- box-sizing: border-box;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .live-board-content {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 10px;
- }
- .live-icon {
- flex-shrink: 0;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .live-info {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: center;
- gap: 2px;
- font-size: 15px;
- color: #222;
- }
- .live-url,
- .live-audio {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 180px;
- }
- .label {
- color: #e67e22;
- font-weight: bold;
- margin-right: 4px;
- }
- .live-board-wrapper.selected {
- border-color: #409eff;
- }
- .live-icon {
- margin-right: 10px;
- }
- .live-info {
- font-size: 13px;
- color: #e67e22;
- margin-top: 4px;
- text-align: center;
- }
- .live-url {
- font-size: 13px;
- color: #e67e22;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 120px;
- }
- .live-audio {
- font-size: 12px;
- color: #888;
- margin-top: 4px;
- }
- .resize-handle {
- width: 8px;
- height: 8px;
- background: #fff;
- border: 1.5px solid #e67e22;
- border-radius: 50%;
- position: absolute;
- z-index: 2;
- }
- .resize-nw {
- top: -4px;
- left: -4px;
- cursor: nw-resize;
- }
- .resize-ne {
- top: -4px;
- right: -4px;
- cursor: ne-resize;
- }
- .resize-sw {
- bottom: -4px;
- left: -4px;
- cursor: sw-resize;
- }
- .resize-se {
- bottom: -4px;
- right: -4px;
- cursor: se-resize;
- }
- .live-icon {
- margin-right: 10px;
- }
- .live-info {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- justify-content: center;
- }
- .live-url {
- font-size: 13px;
- color: #e67e22;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 120px;
- }
- .live-audio {
- font-size: 12px;
- color: #888;
- margin-top: 4px;
- }
- .resize-handle {
- width: 8px;
- height: 8px;
- background: #fff;
- border: 1.5px solid #e67e22;
- border-radius: 50%;
- position: absolute;
- z-index: 2;
- }
- .resize-nw {
- top: -4px;
- left: -4px;
- cursor: nw-resize;
- }
- .resize-ne {
- top: -4px;
- right: -4px;
- cursor: ne-resize;
- }
- .resize-sw {
- bottom: -4px;
- left: -4px;
- cursor: sw-resize;
- }
- .resize-se {
- bottom: -4px;
- right: -4px;
- cursor: se-resize;
- }
- </style>
|