|
|
@@ -2,12 +2,35 @@
|
|
|
<div class="web-preview-panel">
|
|
|
<!-- 地址栏:面板里已经在浏览的真实地址(同源,可读 location),随时能复制或跳回新窗口 -->
|
|
|
<div class="web-toolbar">
|
|
|
- <div class="web-url" :title="url">{{ displayUrl }}</div>
|
|
|
- <a-select v-model:value="sizeKey" class="web-size-select" size="small" :options="sizeOptions" :dropdown-match-select-width="false" />
|
|
|
- <div class="web-actions">
|
|
|
- <a-button size="small" type="link" @click="reload">刷新</a-button>
|
|
|
- <a-button size="small" type="link" @click="openInBrowser">新窗口打开</a-button>
|
|
|
- <a-button size="small" type="link" @click="copyUrl">复制链接</a-button>
|
|
|
+ <div class="web-address-row">
|
|
|
+ <div class="web-url" :title="url">
|
|
|
+ <span class="web-url-icon"></span>
|
|
|
+ <span class="web-url-text">{{ displayUrl }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="web-controls-row">
|
|
|
+ <a-select
|
|
|
+ v-model:value="sizeKey"
|
|
|
+ class="web-size-select"
|
|
|
+ size="small"
|
|
|
+ :options="sizeOptions"
|
|
|
+ :dropdown-match-select-width="false"
|
|
|
+ dropdown-class-name="web-size-dropdown"
|
|
|
+ />
|
|
|
+ <div class="web-actions">
|
|
|
+ <button type="button" class="web-action-btn" title="刷新" @click="reload">
|
|
|
+ <span class="web-action-icon web-action-icon-refresh"></span>
|
|
|
+ <span>刷新</span>
|
|
|
+ </button>
|
|
|
+ <button type="button" class="web-action-btn" title="在新窗口打开" @click="openInBrowser">
|
|
|
+ <span class="web-action-icon web-action-icon-export"></span>
|
|
|
+ <span>新窗口</span>
|
|
|
+ </button>
|
|
|
+ <button type="button" class="web-action-btn" title="复制链接" @click="copyUrl">
|
|
|
+ <span class="web-action-icon web-action-icon-copy"></span>
|
|
|
+ <span>复制链接</span>
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<!--
|
|
|
@@ -16,7 +39,10 @@
|
|
|
只缩不放(放大会糊),四周留白居中;选「自适应面板」时退回铺满、不缩放。
|
|
|
-->
|
|
|
<div ref="stageRef" class="web-stage">
|
|
|
- <div v-if="loading" class="web-loading">正在加载页面…</div>
|
|
|
+ <div v-if="loading" class="web-loading">
|
|
|
+ <span class="web-loading-spinner"></span>
|
|
|
+ <span>正在加载页面…</span>
|
|
|
+ </div>
|
|
|
<div class="web-viewport" :class="{ 'is-loading': loading }" :style="viewportStyle">
|
|
|
<iframe ref="frameRef" class="web-frame" :src="url" frameborder="0" @load="loading = false"></iframe>
|
|
|
</div>
|
|
|
@@ -90,11 +116,14 @@
|
|
|
resizeObserver = null;
|
|
|
});
|
|
|
|
|
|
- // 标签栏里显示短路径,完整地址放在 title 上
|
|
|
+ // 地址栏文案:同源页面显示路径,外部页面显示域名 + 路径;完整地址放在 title 上。
|
|
|
+ // 之前只取 pathname,外部站点根路径会显示成孤零零的 "/"。
|
|
|
const displayUrl = computed(() => {
|
|
|
try {
|
|
|
const u = new URL(props.url, window.location.href);
|
|
|
- return u.pathname + u.search || u.href;
|
|
|
+ const path = `${u.pathname}${u.search}${u.hash}`;
|
|
|
+ if (u.origin === window.location.origin) return path || '/';
|
|
|
+ return `${u.host}${path === '/' ? '' : path}`;
|
|
|
} catch {
|
|
|
return props.url;
|
|
|
}
|
|
|
@@ -144,39 +173,183 @@
|
|
|
flex-direction: column;
|
|
|
height: 100%;
|
|
|
min-height: 0;
|
|
|
+ background: linear-gradient(180deg, rgb(8 37 58 / 98%), rgb(4 22 36 / 98%));
|
|
|
}
|
|
|
|
|
|
.web-toolbar {
|
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
+ flex-direction: column;
|
|
|
gap: 8px;
|
|
|
flex-shrink: 0;
|
|
|
- padding: 8px 10px;
|
|
|
- border-bottom: 1px solid rgb(63 80 106 / 25%);
|
|
|
+ padding: 10px 12px;
|
|
|
+ border-bottom: 1px solid rgb(63 80 106 / 35%);
|
|
|
+ background: rgb(2 18 30 / 35%);
|
|
|
+ }
|
|
|
|
|
|
- .web-url {
|
|
|
- flex: 1;
|
|
|
+ .web-address-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-url {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ min-width: 0;
|
|
|
+ height: 30px;
|
|
|
+ padding: 0 10px;
|
|
|
+ border: 1px solid rgb(63 80 106 / 50%);
|
|
|
+ border-radius: 6px;
|
|
|
+ background: rgb(1 16 27 / 72%);
|
|
|
+ color: #c9d1d9;
|
|
|
+ font-size: 12px;
|
|
|
+ transition:
|
|
|
+ border-color 0.18s,
|
|
|
+ background 0.18s;
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: rgb(10 132 255 / 40%);
|
|
|
+ background: rgb(1 20 34 / 90%);
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-url-icon {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ background-image: var(--img-chat-link-icon);
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ opacity: 0.8;
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-url-text {
|
|
|
min-width: 0;
|
|
|
- font-size: 12px;
|
|
|
- color: #8b949e;
|
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
|
white-space: nowrap;
|
|
|
- padding: 2px 8px;
|
|
|
- border-radius: 4px;
|
|
|
- background: rgb(255 255 255 / 5%);
|
|
|
+ letter-spacing: 0.2px;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- .web-size-select {
|
|
|
- width: 116px;
|
|
|
- flex-shrink: 0;
|
|
|
+ .web-controls-row {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-size-select {
|
|
|
+ width: 118px;
|
|
|
+ height: 28px;
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
+ :deep(.zxm-select-selector) {
|
|
|
+ height: 28px !important;
|
|
|
+ padding: 0 9px !important;
|
|
|
+ border-color: rgb(63 80 106 / 50%) !important;
|
|
|
+ border-radius: 6px !important;
|
|
|
+ background: rgb(1 16 27 / 72%) !important;
|
|
|
+ box-shadow: none !important;
|
|
|
+ transition:
|
|
|
+ border-color 0.18s,
|
|
|
+ background 0.18s;
|
|
|
}
|
|
|
|
|
|
- .web-actions {
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 2px;
|
|
|
+ :deep(.zxm-select-selection-item) {
|
|
|
+ color: #c9d1d9 !important;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 26px !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.zxm-select-arrow) {
|
|
|
+ color: #8b949e !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover :deep(.zxm-select-selector) {
|
|
|
+ border-color: rgb(10 132 255 / 40%) !important;
|
|
|
+ background: rgb(1 20 34 / 90%) !important;
|
|
|
+ }
|
|
|
+
|
|
|
+ &.zxm-select-focused :deep(.zxm-select-selector) {
|
|
|
+ border-color: #5cb8ff !important;
|
|
|
+ box-shadow: 0 0 0 2px rgb(10 132 255 / 15%) !important;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ margin-left: auto;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-action-btn {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ height: 28px;
|
|
|
+ padding: 0 9px;
|
|
|
+ border: 1px solid transparent;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: transparent;
|
|
|
+ color: #8b949e;
|
|
|
+ font-size: 12px;
|
|
|
+ line-height: 1;
|
|
|
+ white-space: nowrap;
|
|
|
+ cursor: pointer;
|
|
|
+ transition:
|
|
|
+ color 0.18s,
|
|
|
+ background 0.18s,
|
|
|
+ border-color 0.18s;
|
|
|
+
|
|
|
+ .web-action-icon {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
flex-shrink: 0;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-size: 100% 100%;
|
|
|
+ opacity: 0.7;
|
|
|
+ transition: opacity 0.18s;
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-action-icon-refresh {
|
|
|
+ background-image: var(--img-chat-regenerate-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-action-icon-export {
|
|
|
+ background-image: var(--img-chat-export-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ .web-action-icon-copy {
|
|
|
+ background-image: var(--img-chat-copy-icon);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:hover {
|
|
|
+ border-color: rgb(10 132 255 / 22%);
|
|
|
+ background: rgb(10 132 255 / 12%);
|
|
|
+ color: #e0e6ed;
|
|
|
+
|
|
|
+ .web-action-icon {
|
|
|
+ opacity: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ &:active {
|
|
|
+ background: rgb(10 132 255 / 20%);
|
|
|
+ }
|
|
|
+
|
|
|
+ &:focus-visible {
|
|
|
+ outline: 1px solid #5cb8ff;
|
|
|
+ outline-offset: 1px;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* 鼠标点击后不保留持续高亮,只有键盘聚焦才显示描边 */
|
|
|
+ &:focus:not(:focus-visible) {
|
|
|
+ outline: none;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -184,17 +357,24 @@
|
|
|
position: relative;
|
|
|
flex: 1;
|
|
|
min-height: 0;
|
|
|
+ margin: 10px 12px 12px;
|
|
|
overflow: hidden;
|
|
|
+ border: 1px solid rgb(63 80 106 / 35%);
|
|
|
+ border-radius: 8px;
|
|
|
// 缩放后四周的留白底色,跟面板区分开,看起来像「一块屏幕」
|
|
|
- background: rgb(0 0 0 / 22%);
|
|
|
+ background: radial-gradient(ellipse at 50% 0%, rgb(10 132 255 / 8%), transparent 60%), #03111d;
|
|
|
+ box-shadow:
|
|
|
+ inset 0 0 0 1px rgb(255 255 255 / 2%),
|
|
|
+ 0 8px 24px rgb(0 0 0 / 25%);
|
|
|
}
|
|
|
|
|
|
.web-viewport {
|
|
|
position: absolute;
|
|
|
transform-origin: center center;
|
|
|
overflow: hidden;
|
|
|
+ border-radius: 4px;
|
|
|
background: #fff;
|
|
|
- box-shadow: 0 6px 22px rgb(0 0 0 / 40%);
|
|
|
+ box-shadow: 0 8px 28px rgb(0 0 0 / 42%);
|
|
|
|
|
|
&.is-loading {
|
|
|
visibility: hidden;
|
|
|
@@ -211,10 +391,57 @@
|
|
|
.web-loading {
|
|
|
position: absolute;
|
|
|
inset: 0;
|
|
|
+ z-index: 1;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- font-size: 13px;
|
|
|
+ gap: 8px;
|
|
|
+ background: rgb(3 17 29 / 40%);
|
|
|
color: #8b949e;
|
|
|
+ font-size: 12px;
|
|
|
+
|
|
|
+ .web-loading-spinner {
|
|
|
+ width: 14px;
|
|
|
+ height: 14px;
|
|
|
+ border: 2px solid rgb(92 184 255 / 20%);
|
|
|
+ border-top-color: #5cb8ff;
|
|
|
+ border-radius: 50%;
|
|
|
+ animation: web-loading-spin 0.8s linear infinite;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @keyframes web-loading-spin {
|
|
|
+ to {
|
|
|
+ transform: rotate(360deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|
|
|
+
|
|
|
+<style lang="less">
|
|
|
+ /* 下拉挂在 body 下,不能用 scoped;与面板深色风格保持一致 */
|
|
|
+ .web-size-dropdown {
|
|
|
+ padding: 4px;
|
|
|
+ border: 1px solid #1a4a6f;
|
|
|
+ border-radius: 6px;
|
|
|
+ background: #0a2a3f;
|
|
|
+ box-shadow: 0 6px 20px rgb(0 0 0 / 42%);
|
|
|
+
|
|
|
+ .zxm-select-item {
|
|
|
+ min-height: 28px;
|
|
|
+ padding: 4px 9px;
|
|
|
+ border-radius: 4px;
|
|
|
+ color: #c9d1d9;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .zxm-select-item-option-active:not(.zxm-select-item-option-disabled) {
|
|
|
+ background: rgb(10 132 255 / 12%);
|
|
|
+ }
|
|
|
+
|
|
|
+ .zxm-select-item-option-selected:not(.zxm-select-item-option-disabled) {
|
|
|
+ background: rgb(10 132 255 / 18%);
|
|
|
+ color: #e0e6ed;
|
|
|
+ font-weight: 400;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|