修改访问客制站点功能技术架构前的存档
parent
ddfba0ae1f
commit
c76aa923d4
@ -0,0 +1,23 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { getMenus } from '@renderer/api/api'
|
||||
|
||||
export const useMenusStore = defineStore('Menus', {
|
||||
state: () => {
|
||||
return {
|
||||
menus: []
|
||||
}
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
async getMenusAction() {
|
||||
let res = await getMenus()
|
||||
// console.log(res.data)
|
||||
this.menus = res.data.data.slice(0, res.data.data.length)
|
||||
},
|
||||
getMenusDebug(items) {
|
||||
// let res = getMenus()
|
||||
// console.log(res.data)
|
||||
this.menus = items.slice(0, items.length)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -0,0 +1,52 @@
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
interface Tab {
|
||||
id: string
|
||||
url: string
|
||||
title: string
|
||||
active: boolean
|
||||
}
|
||||
|
||||
export const useTabsStore = defineStore('tabs', {
|
||||
state: () => ({
|
||||
tabs: [] as Tab[],
|
||||
currentTabId: null as string | null
|
||||
}),
|
||||
actions: {
|
||||
addTab(item: { platformUrl: string; platformName: string }) {
|
||||
const existingTab = this.tabs.find(tab => tab.url === item.platformUrl)
|
||||
|
||||
if (existingTab) {
|
||||
this.currentTabId = existingTab.id
|
||||
return existingTab.id
|
||||
}
|
||||
|
||||
const newTab = {
|
||||
id: Date.now().toString(),
|
||||
url: item.platformUrl,
|
||||
title: item.platformName,
|
||||
active: true
|
||||
}
|
||||
|
||||
this.tabs.forEach(tab => tab.active = false)
|
||||
this.tabs.push(newTab)
|
||||
this.currentTabId = newTab.id
|
||||
return newTab.id
|
||||
},
|
||||
setActiveTab(tabId: string) {
|
||||
this.tabs.forEach(tab => {
|
||||
tab.active = tab.id === tabId
|
||||
})
|
||||
this.currentTabId = tabId
|
||||
},
|
||||
closeTab(tabId: string) {
|
||||
this.tabs = this.tabs.filter(tab => tab.id !== tabId)
|
||||
if (this.currentTabId === tabId) {
|
||||
this.currentTabId = this.tabs[0]?.id || null
|
||||
}
|
||||
}
|
||||
},
|
||||
getters: {
|
||||
activeTab: (state) => state.tabs.find(tab => tab.id === state.currentTabId)
|
||||
}
|
||||
})
|
||||
@ -0,0 +1,6 @@
|
||||
// event-bus.js
|
||||
import mitt from 'mitt'
|
||||
|
||||
const emitter = mitt()
|
||||
|
||||
export default emitter
|
||||
@ -1,153 +1,181 @@
|
||||
<script setup lang="ts">
|
||||
interface Item {
|
||||
platformUrl: string
|
||||
platformId: number
|
||||
platformName: string
|
||||
platformIcon: string
|
||||
platformUrl: string
|
||||
platformId: number
|
||||
platformName: string
|
||||
platformIcon: string
|
||||
}
|
||||
|
||||
const items: Item[] = [
|
||||
{
|
||||
"platformUrl": "https://etax.zhejiang.chinatax.gov.cn:8443/",
|
||||
"platformId": 1,
|
||||
"platformName": "浙江电子税务局",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/zj-tax.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://portal.zjzwfw.gov.cn/pc/portal/hall/#/MainLobby",
|
||||
"platformId": 2,
|
||||
"platformName": "浙江政务服务网",
|
||||
"platformIcon": "https://zjjcmspublic.oss-cn-hangzhou-zwynet-d01-a.internet.cloud.zj.gov.cn/jcms_files/jcms1/web1/site/script/zjservice/resources/index1/newImg/zjzwLogo.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://www.zjzwfw.gov.cn/zjzwfw/",
|
||||
"platformId": 3,
|
||||
"platformName": "浙里办",
|
||||
"platformIcon": "https://zjjcmspublic.oss-cn-hangzhou-zwynet-d01-a.internet.cloud.zj.gov.cn/jcms_files/jcms1/web1/site/script/zjservice/resources/index1/newImg/zjzwLogo.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://etax.chinatax.gov.cn",
|
||||
"platformId": 4,
|
||||
"platformName": "自然人电子税务局",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/personal-tax.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://www.baidu.com/",
|
||||
"platformId": 5,
|
||||
"platformName": "百度",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/personal-tax.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://https://chat.deepseek.com/",
|
||||
"platformId": 6,
|
||||
"platformName": "Deepseek",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/personal-tax.png"
|
||||
}
|
||||
{
|
||||
"platformUrl": "https://etax.zhejiang.chinatax.gov.cn:8443/",
|
||||
"platformId": 1,
|
||||
"platformName": "浙江电子税务局",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/zj-tax.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://www.zjzwfw.gov.cn/zjservice-fe/#/home",
|
||||
"platformId": 2,
|
||||
"platformName": "浙江政务服务网",
|
||||
"platformIcon": "https://zjjcmspublic.oss-cn-hangzhou-zwynet-d01-a.internet.cloud.zj.gov.cn/jcms_files/jcms1/web1/site/script/zjservice/resources/index1/newImg/zjzwLogo.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://www.zjzwfw.gov.cn/zjzwfw/",
|
||||
"platformId": 3,
|
||||
"platformName": "浙里办",
|
||||
"platformIcon": "https://zjjcmspublic.oss-cn-hangzhou-zwynet-d01-a.internet.cloud.zj.gov.cn/jcms_files/jcms1/web1/site/script/zjservice/resources/index1/newImg/zjzwLogo.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://etax.chinatax.gov.cn",
|
||||
"platformId": 4,
|
||||
"platformName": "自然人电子税务局",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/personal-tax.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://www.baidu.com/",
|
||||
"platformId": 5,
|
||||
"platformName": "百度",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/personal-tax.png"
|
||||
},
|
||||
{
|
||||
"platformUrl": "https://https://chat.deepseek.com/",
|
||||
"platformId": 6,
|
||||
"platformName": "Deepseek",
|
||||
"platformIcon": "http://192.168.0.117:8092/images/personal-tax.png"
|
||||
}
|
||||
]
|
||||
|
||||
import { NButton } from 'naive-ui';
|
||||
import router from '@renderer/router'
|
||||
import { NIcon } from 'naive-ui';
|
||||
import { ArrowBackCircleOutline, ArrowForwardCircleOutline } from '@vicons/ionicons5'
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useMenusStore } from '@renderer/store/menus'
|
||||
|
||||
const menusStore = useMenusStore()
|
||||
const currentIndex = ref(0)
|
||||
const visibleCount = ref(4)
|
||||
let currentButtonList: Item[] = []
|
||||
const currentButtonList = ref<Item[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
initButtonList()
|
||||
// initButtonList()
|
||||
initButtonDebug()
|
||||
})
|
||||
|
||||
const initButtonList = () => {
|
||||
currentButtonList = items.slice(0, 3)
|
||||
console.log(currentButtonList)
|
||||
const initButtonList = async () => {
|
||||
await menusStore.getMenusAction()
|
||||
if (menusStore.menus.length > 0) {
|
||||
currentButtonList.value = menusStore.menus.slice(0, 4)
|
||||
visibleCount.value = menusStore.menus.length
|
||||
}
|
||||
// currentButtonList.value = items.slice(0, 4)
|
||||
console.log(currentButtonList.value)
|
||||
}
|
||||
|
||||
const initButtonDebug = () => {
|
||||
menusStore.getMenusDebug(items)
|
||||
if (menusStore.menus.length > 0) {
|
||||
currentButtonList.value = menusStore.menus.slice(0, 4)
|
||||
visibleCount.value = menusStore.menus.length
|
||||
}
|
||||
}
|
||||
|
||||
const handleClick = (item) => {
|
||||
console.log(item.platformUrl)
|
||||
router.push({
|
||||
path: '/home/webContainer',
|
||||
query: item
|
||||
})
|
||||
}
|
||||
|
||||
const goLeft = () => {
|
||||
currentIndex.value--
|
||||
currentButtonList = items.slice(currentIndex.value, currentIndex.value + 3)
|
||||
console.log(currentIndex.value)
|
||||
currentIndex.value--
|
||||
currentButtonList.value = menusStore.menus.slice(currentIndex.value, currentIndex.value + 4)
|
||||
}
|
||||
|
||||
const goRight = () => {
|
||||
currentIndex.value++
|
||||
currentButtonList = items.slice(currentIndex.value, currentIndex.value + 3)
|
||||
console.log(currentIndex.value)
|
||||
currentIndex.value++
|
||||
currentButtonList.value = menusStore.menus.slice(currentIndex.value, currentIndex.value + 4)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="menu-bg">
|
||||
<div class="side-div">
|
||||
<NButton @click="goLeft" :disabled="currentIndex <= 0">{{ '<' }}</NButton>
|
||||
</div>
|
||||
<div class="center-div">
|
||||
<div v-for="(item, index) in currentButtonList" :key="index" class="custom-button" @click="handleClick(item)">
|
||||
<img :src="item.platformIcon" alt="" class="button-image">
|
||||
<span class="button-text">{{ item.platformName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-div">
|
||||
<NButton @click="goRight" :disabled="currentIndex >= 6">{{ '>' }}</NButton>
|
||||
</div>
|
||||
<div class="menu-bg">
|
||||
<div class="side-div">
|
||||
<a @click="goLeft" v-show="!(currentIndex <= 0)">
|
||||
<n-icon size="50">
|
||||
<ArrowBackCircleOutline />
|
||||
</n-icon>
|
||||
</a>
|
||||
</div>
|
||||
<div class="center-div">
|
||||
<div v-for="(item, index) in currentButtonList" :key="index" class="custom-button" @click="handleClick(item)">
|
||||
<img :src="item.platformIcon" alt="" class="button-image">
|
||||
<span class="button-text">{{ item.platformName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="side-div">
|
||||
<a @click="goRight" v-show="!(currentIndex >= visibleCount - 4)">
|
||||
<n-icon size="50">
|
||||
<ArrowForwardCircleOutline />
|
||||
</n-icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.menu-bg {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 92vh;
|
||||
width: 100vw;
|
||||
|
||||
.center-div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 92vh;
|
||||
width: 100vw;
|
||||
|
||||
.center-div {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 550px;
|
||||
width: 1100px;
|
||||
background-color: rgba($color: #fff, $alpha: 0.5);
|
||||
flex-direction: row;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
.custom-button {
|
||||
display: flex;
|
||||
flex: 0 0 20%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50%;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
background-color: #6699ff;
|
||||
|
||||
.button-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.button-text {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-button:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
height: 550px;
|
||||
width: 1100px;
|
||||
background-color: rgba($color: #fff, $alpha: 0.5);
|
||||
flex-direction: row;
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
.custom-button {
|
||||
display: flex;
|
||||
flex: 0 0 20%;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 50%;
|
||||
padding: 10px;
|
||||
margin: 20px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
background-color: #6699ff;
|
||||
|
||||
.button-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: contain;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.button-text {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.side-div {
|
||||
margin: 40px;
|
||||
.custom-button:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.side-div {
|
||||
margin: 40px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,7 +1,168 @@
|
||||
<script setup lang="ts">
|
||||
defineOptions({
|
||||
name: 'WebContainer'
|
||||
})
|
||||
import { onBeforeUnmount, onMounted, watch, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useTabsStore } from '@renderer/store/tabs'
|
||||
import { NIcon } from 'naive-ui'
|
||||
import { CloseCircleOutline } from '@vicons/ionicons5'
|
||||
import eventBus from '@renderer/utils/eventBus'
|
||||
|
||||
const route = useRoute()
|
||||
const tabsStore = useTabsStore()
|
||||
// const webviewRefs = ref<webview[]>([])
|
||||
// const tabs = ref([...]) // 你的标签页数据
|
||||
|
||||
// Initialize with current route item
|
||||
watch(() => route.query, (newQuery) => {
|
||||
if (newQuery.platformUrl) {
|
||||
tabsStore.addTab({
|
||||
platformUrl: newQuery.platformUrl as string,
|
||||
platformName: newQuery.platformName as string
|
||||
})
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
onMounted(() => {
|
||||
eventBus.on('logout-sent', navigateToLogout)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
eventBus.off('logout-sent', navigateToLogout)
|
||||
})
|
||||
|
||||
// 设置 ref 的回调函数
|
||||
// const setWebviewRef = (el) => {
|
||||
// if (el) {
|
||||
// webviewRefs.value.push(el)
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
const navigateToLogout = async () => {
|
||||
// 定义登出脚本
|
||||
const logoutScript = `
|
||||
// 尝试找到登出按钮并点击
|
||||
const logoutSelectors = [
|
||||
'#logout',
|
||||
'[href*="logout"]',
|
||||
'button[onclick*="logout"]',
|
||||
'.logout-btn'
|
||||
]
|
||||
|
||||
let foundLogout = false
|
||||
|
||||
for (const selector of logoutSelectors) {
|
||||
const btn = document.querySelector(selector)
|
||||
if (btn) {
|
||||
btn.click()
|
||||
foundLogout = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 清除本地存储
|
||||
localStorage.clear()
|
||||
sessionStorage.clear()
|
||||
|
||||
// 清除cookies
|
||||
document.cookie.split(";").forEach(c => {
|
||||
document.cookie = c.replace(/^ +/, "")
|
||||
.replace(/=.*/, "=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/")
|
||||
})
|
||||
|
||||
foundLogout
|
||||
`
|
||||
const webviews = document.querySelectorAll('webview')
|
||||
console.log(webviews)
|
||||
// 遍历所有 webview 执行登出
|
||||
webviews.forEach(webview => {
|
||||
webview.addEventListener('close', () => {
|
||||
console.log('webview被关闭')
|
||||
// webview.src = 'about:blank'
|
||||
// webview.executeJavaScript(logoutScript)
|
||||
})
|
||||
// ✅ 正确调用 Electron 的 webview API
|
||||
// webview.executeJavaScript(logoutScript)
|
||||
// .then(success => {
|
||||
// if (success) {
|
||||
// console.log('登出脚本执行成功')
|
||||
// webview.reload(); // 刷新页面
|
||||
// }
|
||||
// })
|
||||
// .catch(err => console.error('脚本执行失败:', err))
|
||||
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>WebContainer</div>
|
||||
</template>
|
||||
<div class="web-container">
|
||||
<div class="tabs">
|
||||
<div v-for="tab in tabsStore.tabs" :key="tab.id" :class="['tab', { active: tab.id === tabsStore.currentTabId }]"
|
||||
@click="tabsStore.setActiveTab(tab.id)">
|
||||
<span class="title-tab">{{ tab.title }}</span>
|
||||
<span class="title-tab close-tab" @click.stop="tabsStore.closeTab(tab.id)">
|
||||
<n-icon :component="CloseCircleOutline" size="10" :depth="3" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="web-content">
|
||||
<webview v-for="tab in tabsStore.tabs" v-show="tab.id === tabsStore.currentTabId" :key="tab.id" :src="tab.url"
|
||||
sandbox="allow-same-origin allow-scripts" frameborder="0" class="webview" :data-tab-id="tab.id"></webview>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.web-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 92vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
background: #f0f0f0;
|
||||
// padding: 8px 8px 0;
|
||||
}
|
||||
|
||||
.tab {
|
||||
padding: 0 6px 0 10px;
|
||||
margin-left: 4px;
|
||||
background: #ddd;
|
||||
color: #000;
|
||||
border-radius: 4px 4px 0 0;
|
||||
cursor: pointer;
|
||||
|
||||
.title-tab {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.close-tab {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close-tab:hover {
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.tab.active {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.web-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.webview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue