Commit 3e56bc3a authored by Wallen姚文辉's avatar Wallen姚文辉

UI自动化版本

parent 03cd23a1
......@@ -12,6 +12,10 @@
"@grapecity-software/spread-sheets-resources-zh": "^17.1.4",
"@grapecity-software/spread-sheets-vue": "^17.1.4",
"@novnc/novnc": "^1.4.0",
"@vue-flow/background": "^1.3.0",
"@vue-flow/controls": "^1.1.2",
"@vue-flow/core": "^1.41.1",
"@vue-flow/node-resizer": "^1.4.0",
"balm-ui": "^10.28.0",
"clipboard": "^2.0.11",
"core-js": "^3.8.3",
......@@ -27,6 +31,7 @@
"vanta": "^0.5.24",
"view-ui-plus": "^1.3.16",
"vue": "^3.2.13",
"vue-clipboard3": "^2.0.0",
"vue-echarts": "^6.7.2",
"vue-router": "^4.0.3",
"vue-socket.io": "^3.0.10",
......@@ -3197,6 +3202,11 @@
"@types/node": "*"
}
},
"node_modules/@types/web-bluetooth": {
"version": "0.0.20",
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmmirror.com/@types/ws/-/ws-8.5.10.tgz",
......@@ -3206,6 +3216,51 @@
"@types/node": "*"
}
},
"node_modules/@vue-flow/background": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vue-flow/background/-/background-1.3.0.tgz",
"integrity": "sha512-fu/8s9wzSOQIitnSTI10XT3bzTtagh4h8EF2SWwtlDklOZjAaKy75lqv4htHa3wigy/r4LGCOGwLw3Pk88/AxA==",
"peerDependencies": {
"@vue-flow/core": "^1.23.0",
"vue": "^3.3.0"
}
},
"node_modules/@vue-flow/controls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@vue-flow/controls/-/controls-1.1.2.tgz",
"integrity": "sha512-6dtl/JnwDBNau5h3pDBdOCK6tdxiVAOL3cyruRL61gItwq5E97Hmjmj2BIIqX2p7gU1ENg3z80Z4zlu58fGlsg==",
"peerDependencies": {
"@vue-flow/core": "^1.23.0",
"vue": "^3.3.0"
}
},
"node_modules/@vue-flow/core": {
"version": "1.41.1",
"resolved": "https://registry.npmjs.org/@vue-flow/core/-/core-1.41.1.tgz",
"integrity": "sha512-/w/FHXxnOE1QFsEQMVJGD4DGinlhj8ZDJmumWMFNJztk94d28iK8BWRQD4Cy305Jnt29sf3owHgliGXHIZDLTg==",
"dependencies": {
"@vueuse/core": "^10.5.0",
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0"
},
"peerDependencies": {
"vue": "^3.3.0"
}
},
"node_modules/@vue-flow/node-resizer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@vue-flow/node-resizer/-/node-resizer-1.4.0.tgz",
"integrity": "sha512-S52MRcSpd6asza8Cl0bKM2sHGrbq7vBydKHDuPdoTD+cvjNX6XF4LSiPZOuzExePI6b+O6dg2EZ1378oOLGFpA==",
"dependencies": {
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0"
},
"peerDependencies": {
"@vue-flow/core": "^1.23.0",
"vue": "^3.3.0"
}
},
"node_modules/@vue/babel-helper-vue-jsx-merge-props": {
"version": "1.4.0",
"resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz",
......@@ -3985,6 +4040,89 @@
"integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==",
"dev": true
},
"node_modules/@vueuse/core": {
"version": "10.11.1",
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.11.1.tgz",
"integrity": "sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==",
"dependencies": {
"@types/web-bluetooth": "^0.0.20",
"@vueuse/metadata": "10.11.1",
"@vueuse/shared": "10.11.1",
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/core/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@vueuse/metadata": {
"version": "10.11.1",
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.11.1.tgz",
"integrity": "sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==",
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared": {
"version": "10.11.1",
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.11.1.tgz",
"integrity": "sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==",
"dependencies": {
"vue-demi": ">=0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/@vueuse/shared/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@webassemblyjs/ast": {
"version": "1.12.1",
"resolved": "https://registry.npmmirror.com/@webassemblyjs/ast/-/ast-1.12.1.tgz",
......@@ -5992,6 +6130,102 @@
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-dispatch": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz",
"integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-drag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz",
"integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-selection": "3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-selection": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz",
"integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-transition": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz",
"integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==",
"dependencies": {
"d3-color": "1 - 3",
"d3-dispatch": "1 - 3",
"d3-ease": "1 - 3",
"d3-interpolate": "1 - 3",
"d3-timer": "1 - 3"
},
"engines": {
"node": ">=12"
},
"peerDependencies": {
"d3-selection": "2 - 3"
}
},
"node_modules/d3-zoom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz",
"integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==",
"dependencies": {
"d3-dispatch": "1 - 3",
"d3-drag": "2 - 3",
"d3-interpolate": "1 - 3",
"d3-selection": "2 - 3",
"d3-transition": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/data-view-buffer": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz",
......@@ -13426,6 +13660,14 @@
}
}
},
"node_modules/vue-clipboard3": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/vue-clipboard3/-/vue-clipboard3-2.0.0.tgz",
"integrity": "sha512-Q9S7dzWGax7LN5iiSPcu/K1GGm2gcBBlYwmMsUc5/16N6w90cbKow3FnPmPs95sungns4yvd9/+JhbAznECS2A==",
"dependencies": {
"clipboard": "^2.0.6"
}
},
"node_modules/vue-echarts": {
"version": "6.7.2",
"resolved": "https://registry.npmjs.org/vue-echarts/-/vue-echarts-6.7.2.tgz",
......
......@@ -14,6 +14,8 @@ import TestReport from '@/views/tool/TestReport';
import ReportLsit from '@/views/tool/ReportLsit';
import AduitReportList from '@/views/tool/AduitReportList';
import TestCase from '@/views/autotest/TestCase.vue';
import RequestsTemplate from '@/views/tool/RequestsTemplate.vue';
import MoveUIRun from '@/views/autotest/MoveUIRun.vue';
import Echarts from "vue-echarts";
import 'echarts';
import ViewUIPlus from 'view-ui-plus';
......@@ -85,7 +87,16 @@ const routers = [
path: 'testcase',
name: 'testcase',
component: TestCase
}
},{
path:'moveuirun',
name:'moveuirun',
component: MoveUIRun
},
// {
// path:'requestsTemplate',
// name:'requestsTemplate',
// component:RequestsTemplate
// },
]
axios.interceptors.request.use(function (config) {
......
<template>
<PageHeader :title="title">
<template #content>
<Table border :columns="columns" :data="usbInfo.arr" width="800">
<template #info="{ row }">
<p v-for="item in row.info">{{ item }}</p>
</template>
<template #status="{ row }">
<div v-if="row.status">
<Icon type="md-analytics" color="#19be6b" /><span>已连接</span>
</div>
<div v-else>
<Icon type="ios-alert" color="#ed4014" /><span>未连接</span>
</div>
</template>
<template #edit="{ row }">
<Button v-if="!row.status" @click="bindusb(row.key)">绑定</Button>
</template>
</Table>
</template>
<template #extra>
<div>
<div style="text-align: center">
<span style="color: #17233d;margin-right: 10px;">远端状态:</span>
<div v-if="connectSatus" style="display: inline;">
<span style="margin-right: 10px;color: #19be6b;">连接中</span>
<Button type="info" @click="killssh">断开连接</Button>
</div>
<div v-else style="display: inline;">
<Icon type="md-warning" color="#ed4014" size="25" /><span style="color: #ed4014;">未连接</span>
</div>
</div>
<div v-if="!connectSatus">
<div>
<span style="color: #17233d;margin-right: 10px;">连接命令:</span>
<span style="margin-right: 10px;">ssh -N -R 3240:localhost:3240 root@192.168.11.185</span>
<Button style="margin-right: 10px;" type="info" @click="copyConnectCmd">复制</Button>
</div>
<div style="text-align: center;">
<Button style="margin-top: 5px;margin-right: 10px;" type="info"
@click="copyConnectPass">复制连接密码</Button>
</div>
</div>
<div style="text-align: center;margin-top: 5px;">
<div v-if="connectSatus">
<span style="color: #17233d;margin-right: 10px;">appium:</span>
<div v-if="appiumStatus" style="display: inline;">
<span style="margin-right: 10px;color: #19be6b;">已启动</span>
<Button style="margin-right: 10px;" type="info" @click="restartAppium"
:disabled="canRestartAppium">重启</Button>
<Button type="info" @click="">查看日志</Button>
</div>
<div v-else style="display: inline;">
<span style="margin-right: 10px;color: #19be6b;">未启动</span>
<Button type="info" @click="startAppium" :disabled="canRestartAppium">启动</Button>
</div>
</div>
</div>
</div>
</template>
</PageHeader>
<div v-if="usbInfo.arr.filter(item => item.status == 1).length">
<Row style="margin-top: 20px;">
<Col :span="phoneStyle.android.span" :offset="phoneStyle.android.offset">
<div v-if="selectSystem == 0 || selectSystem == 1" class="phone">
<Icon style="margin-top: 50px;" type="logo-android" size="100" color="#19be6b" />
<p v-if="!androidConnectInfo.adbinfo" style="color: #ff9900;">请确保已打开usb调试模式</p>
<Button v-if="selectSystem == 0" style="margin-top: 30px;" @click="connectAndroid">连接</Button>
<p v-if="androidConnectInfo.adbinfo" style="font-size: 20px;color:#2d8cf0">adb信息</p>
<p v-if="androidConnectInfo.adbinfo">{{ androidConnectInfo.adbinfo }}</p>
<p v-if="Object.getOwnPropertyNames(androidConnectInfo.appiumArgs).length"
style="font-size: 20px;color:#2d8cf0">appium连接参数</p>
<div style="background-color: #19be6b;margin-left: 5px;margin-right: 5px;margin-top: 10px;">
<div v-for="v, k in androidConnectInfo.appiumArgs">
<p><span style="color: blueviolet; margin-right: 5px;">{{ k }}:</span>
<Tooltip v-if="getlowV(k, v)[0]" content="Top Center text" placement="top"><span>{{
getlowV(k, v)[1] }}</span></Tooltip><span v-else>{{ getlowV(k, v)[1] }}</span>
</p>
</div>
</div>
<p>
<Icon v-if="(!androidConnectInfo.adbinfo || !androidConnectInfo.appiumArgs) && selectSystem == 1"
type="ios-loading" class="ivu-anim-loop" size="24" />
</p>
<div>
<Button style="margin-right: 5px;margin-top: 10px;"
v-if="Object.getOwnPropertyNames(androidConnectInfo.appiumArgs).length"
@click="copyappiumconn">复制参数</Button>
<Button v-if="selectSystem == 1" style="margin-top: 10px;" @click="disconnectAndroid">断开连接</Button>
</div>
</div>
</Col>
<Col :span="phoneStyle.ios.span" :offset="phoneStyle.ios.offset">
<div v-if="selectSystem == 0 || selectSystem == 2" class="phone" style="border-color: #c5c8ce;">
<Icon style="margin-top: 50px;" type="logo-apple" size="100" color="#808695" />
<Input v-model="webdriveragentId" placeholder="请输入webdriveragentId" style="width: 200px;" />
<Button v-if="selectSystem == 0" style="margin-top: 10px;" @click="connectIos">连接</Button>
<p>
<Icon v-if="selectSystem == 2 && !iosConnectInfo?.IosId" type="ios-loading" class="ivu-anim-loop"
size="24" />
</p>
<p v-if="iosConnectInfo?.IostestResult == 1" style="color: #ff9900;">请在设备上信任!!</p>
<p v-if="iosConnectInfo?.IostestResult == 3" style="color: #ed4014;">设备的WDAid与实际安装的不一致或WDAid已过期</p>
<p v-if="iosConnectInfo?.IostestResult == 4" style="color: #ed4014;">设备连接异常</p>
<div v-if="iosConnectInfo?.IostestResult == 2"
style="background-color: #19be6b;margin-left: 5px;margin-right: 5px;margin-top: 10px;">
<div v-for="v, k in iosConnectInfo.appiumArgs">
<p><span style="color: blueviolet; margin-right: 5px;">{{ k }}:</span>
<Tooltip v-if="getlowV(k, v)[0]" content="Top Center text" placement="top"><span>{{
getlowV(k, v)[1] }}</span></Tooltip><span v-else>{{ getlowV(k, v)[1] }}</span>
</p>
</div>
</div>
<Button v-if="selectSystem == 2" style="margin-top: 10px;" @click="disconnectIos">断开连接</Button>
</div>
</Col>
</Row>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance, watch, onUnmounted } from 'vue'
// import clipboard from 'clipboard';
import useClipboard from 'vue-clipboard3';
const { proxy, ctx } = getCurrentInstance()
let Message = proxy.$Message
let socket = proxy.$socket
const { toClipboard } = useClipboard();
let appiumStatus = ref(false)
let canRestartAppium = ref(false)
let title = ref('移动端自动化')
let webdriveragentId = ref(null)
let phoneStyle = reactive(
{
android: {
span: "8",
offset: "6"
},
ios: {
span: "8",
offset: "1"
}
}
)
let selectSystem = ref(0)
let connectSatus = ref(false)
let usbInfo = reactive({ arr: [] })
let appiumLogs = reactive({ arr: [] })
let androidConnectInfo = reactive({
adbinfo: null,
appiumArgs: {},
})
let iosConnectInfo = reactive({
// Iostest: false,
// IostestResult: 0,
// IosId: null,
// webDriverAgentID: null,
// startCon: false,
appiumArgs: {}
})
let columns = reactive([
{
title: 'key',
key: 'key',
width: 70
},
{
title: '信息',
slot: 'info'
},
{
title: '状态',
slot: 'status',
width: 100
},
{
title: '操作',
slot: 'edit',
width: 100
},
])
watch(
() => selectSystem.value,
(newVal, oldVal) => {
if (newVal == 0) {
phoneStyle.android = {
span: "8",
offset: "6"
}
phoneStyle.ios = {
span: "8",
offset: "1"
}
} else if (newVal == 1) {
phoneStyle.android = {
span: "8",
offset: "8"
}
} else if ((newVal == 2)) {
phoneStyle.ios = {
span: "8",
offset: "8"
}
}
})
function getlowV(k, v) {
if (k.length + v.length + 2 >= 27) {
return [true, v.slice(0, 25 - k.length - 1) + '...']
} else {
return [false, v]
}
}
async function copyappiumconn() {
const text = JSON.stringify(androidConnectInfo.appiumArgs)
await toClipboard(text)
Message.success("复制成功")
}
function connectAndroid() {
selectSystem.value = 1
socket.on("adbFlush", (data) => {
androidConnectInfo.adbinfo = data
if (data) {
console.log(data.split(' '))
androidConnectInfo.appiumArgs = {
"platformName": "Android",
"appium:Android": 12,
"appium:automationName": "UiAutomator2",
"appium:deviceName": data.split('\t')[0],
"appium:appPackage": "com.galaxy.galaxyandroid",
"appium:appActivity": "com.galaxy.galaxyandroid.SplashActivity",
"appium:remoteAdbHost": "172.17.0.1"
}
}
})
socket.emit("adbInfo")
}
function connectIos() {
selectSystem.value = 2
socket.on('iosinfo', data => {
for (var k in data) {
iosConnectInfo[k]=data[k]
}
})
socket.emit('connectIOS', webdriveragentId.value)
}
function disconnectAndroid() {
selectSystem.value = 0
socket.emit('leaveAdbinfo')
socket.off("adbFlush")
androidConnectInfo.adbinfo = null
androidConnectInfo.appiumArgs = {}
}
function disconnectIos() {
selectSystem.value = 0
if (iosConnectInfo?.IosId ) {
socket.off('iosinfo')
socket.emit('disconnectIOS', webdriveragentId.value)
iosConnectInfo = {
appiumArgs:{}
}
}
}
function restartAppium() {
canRestartAppium.value = true
setTimeout(() => {
canRestartAppium.value = false
}, 5000);
socket.emit("restartAppium")
}
function startAppium() {
canRestartAppium.value = true
setTimeout(() => {
canRestartAppium.value = false
}, 5000);
socket.emit("startAppium")
}
// function disconnectIos() {
// selectSystem.value = 0
// }
async function copyConnectCmd() {
const text = "ssh -N -R 3240:localhost:3240 root@192.168.11.185"
await toClipboard(text)
Message.success("复制成功")
}
async function copyConnectPass() {
const text = "galaxy123"
await toClipboard(text)
Message.success("复制成功")
}
function killssh() {
socket.emit('killproxy')
}
function bindusb(key) {
console.log(key)
socket.emit('connectUsb', key)
}
onMounted(() => {
socket.connect()
socket.on('usbFlush', (data) => {
usbInfo.arr = data
})
socket.on('sshFlush', (data) => {
connectSatus.value = data
})
socket.on('appiumstatus', (data) => {
appiumStatus.value = data
})
socket.on('appiumlog', (data) => {
appiumLogs.arr = data
})
socket.emit("usbInfo")
})
onUnmounted(() => {
disconnectIos()
})
</script>
<style>
.phone {
width: 250px;
height: 500px;
background-color: rgb(231, 246, 255);
border-radius: 20px;
border-style: outset;
border-width: 10px;
border-color: rgb(24, 23, 44);
text-align: center;
}
.transition-box {
margin-top: 32px;
width: 100%;
border-radius: 4px;
background-color: #409EFF;
text-align: center;
color: #fff;
padding: 24px 0;
}
</style>
\ No newline at end of file
......@@ -22,22 +22,24 @@
<Col :lg="4" span="0">
</Col>
<Col :lg="10" span="0">
<Card v-if="choiceId" style="height: 600px;">
<Form >
<Card v-if="choiceId" style="height: 700px">
<Form>
<FormItem>
<Table :show-header="false" row-key="id" :columns="menu_columns"
@on-selection-change="(a) => { select_menu = a }" :data="choicePermissions">
<template #name="{ row }">
<strong>{{ row.name }}</strong>
</template>
<template #elements="{ row }">
<Tag v-for="item in row.elements" :key="item.id" :name="item.id"
:checked="item._checked" checkable
@on-change="(chec, name) => { elechoice(row, chec, name) }" color="primary"
style="margin-right: 5px">
{{ item.name }}</Tag>
</template>
</Table>
<div style="background-color: blue;height: 600px;overflow:auto">
<Table :show-header="false" row-key="id" :columns="menu_columns"
@on-selection-change="(a) => { select_menu = a }" :data="choicePermissions">
<template #name="{ row }">
<strong>{{ row.name }}</strong>
</template>
<template #elements="{ row }">
<Tag v-for="item in row.elements" :key="item.id" :name="item.id"
:checked="item._checked" checkable
@on-change="(chec, name) => { elechoice(row, chec, name) }" color="primary"
style="margin-right: 5px">
{{ item.name }}</Tag>
</template>
</Table>
</div>
</FormItem>
<FormItem style="text-align: right;">
<Button type="primary" @click="updatePermissions(choiceId)">确认</Button>
......@@ -69,7 +71,7 @@ export default {
props: ['isCollapsed',],
data() {
return {
page_id:16,
page_id: 16,
title: "角色管理",
modlShow: false,
addinfo: {
......@@ -109,7 +111,7 @@ export default {
align: 'center'
}
],
elements:[]
elements: []
}
},
methods: {
......@@ -147,7 +149,7 @@ export default {
this.$request("get", "/identity/meun_list", null, this, (data) => { this.allPermissions = data.data })
},
getRolePermissions(id_) {
if (this.elements.indexOf("3-3")==-1) return
if (this.elements.indexOf("3-3") == -1) return
this.$request("get", "/identity/get_permissions/" + id_, null, this, (data) => { a(data.data) })
this.select_element = []
const a = (Permissions) => {
......@@ -187,7 +189,7 @@ export default {
// }
// 编辑权限2,删除权限3,操作栏4
elements(value) {
if (value.indexOf('3-2') != -1 && this.columns.findIndex(each=>{return each.title=='编辑'})==-1) {
if (value.indexOf('3-2') != -1 && this.columns.findIndex(each => { return each.title == '编辑' }) == -1) {
this.columns.push({
title: '编辑',
slot: 'action',
......@@ -201,7 +203,7 @@ export default {
}
},
created() {
this.$getElements(this.page_id,this,(data)=>this.elements=data.data)
this.$getElements(this.page_id, this, (data) => this.elements = data.data)
this.getMenu();
this.getRole();
}
......
<template>
<div class="dnd-flow" @drop="onDrop" :nodes="nodes" @dragover="onDragOver" @dragleave="onDragLeave">
<VueFlow>
<div class="dropzone-background" :style="{
backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
transition: 'background-color 0.2s ease',
}">
<Background :size="2" :gap="20" pattern-color="#BDBDBD">
<div class="overlay">
<slot></slot>
</div>
</Background>
<!-- <template #node-apinode="{aprops}">
<ApiNode :data="aprops.data"></ApiNode>
</template> -->
<!-- <template #node-whilenode="props">
<WhileNode :data="props.data"></WhileNode>
</template> -->
<Controls />
<p v-if="isDragOver">移动到此处</p>
</div>
</VueFlow>
<aside>
<Tabs>
<TabPane label="节点" icon="logo-apple">
<div class="nodes">
<div class="vue-flow__node-input" :draggable="true" @dragstart="onDragStart($event, 'input')">
输入</div>
<div class="vue-flow__node-default" :draggable="true"
@dragstart="onDragStart($event, 'apinode')">接口
</div>
<div class="vue-flow__node-default" :draggable="true"
@dragstart="onDragStart($event, 'default')">数据库
</div>
<div class="vue-flow__node-default" :draggable="true"
@dragstart="onDragStart($event, 'default')">for each
</div>
<div class="vue-flow__node-output" :draggable="true" @dragstart="onDragStart($event, 'whilenode')">
循环
</div>
</div>
</TabPane>
<TabPane label="块" icon="logo-windows">标签二的内容</TabPane>
<TabPane label="线" icon="logo-tux">标签三的内容</TabPane>
<TabPane label="导出" icon="logo-tux">标签三的内容</TabPane>
</Tabs>
</aside>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import { VueFlow, useVueFlow } from '@vue-flow/core'
import {ApiNode } from './myNodeTypes/ApiNode.vue'
import {WhileNode } from './myNodeTypes/WhileNode.vue'
import { Background } from '@vue-flow/background'
import { Controls } from '@vue-flow/controls'
// import SpecialNode from './components/SpecialNode.vue'
// import SpecialEdge from './components/SpecialEdge.vue'
const { onConnect, addEdges, addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow()
const nodes = ref([])
onConnect(addEdges)
let id = 0
function getId() {
return `测试节点${id++}`
}
const state = {
draggedType: ref(null),
isDragOver: ref(false),
isDragging: ref(false)
}
const { draggedType, isDragOver, isDragging } = state
watch(isDragging, (dragging) => {
document.body.style.userSelect = dragging ? 'none' : ''
})
function onDragStart(event, type) {
if (event.dataTransfer) {
event.dataTransfer.setData('application/vueflow', type)
event.dataTransfer.effectAllowed = 'move'
}
draggedType.value = type
isDragging.value = true
document.addEventListener('drop', onDragEnd)
}
function onDragOver(e) {
e.preventDefault()
if (draggedType.value) {
isDragOver.value = true
if (e.dataTransfer) {
e.dataTransfer.dataTransfer = 'move'
}
}
}
function onDragLeave() {
isDragOver.value = false
}
function onDragEnd() {
isDragging.value = false
isDragOver.value = false
draggedType.value = null
document.removeEventListener('drop', onDragEnd)
}
function onDrop(event) {
const position = screenToFlowCoordinate({
x: event.clientX,
y: event.clientY,
})
const nodeId = getId()
const newNode = {
id: nodeId,
type: draggedType.value,
position,
data: { label: nodeId }
}
const { off } = onNodesInitialized(() => {
updateNode(nodeId, node => ({
position: { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 },
}))
off()
})
addNodes(newNode)
}
// these are our edges
</script>
<style>
/* import the necessary styles for Vue Flow to work */
@import '@vue-flow/core/dist/style.css';
/* import the default theme, this is optional but generally recommended */
@import '@vue-flow/core/dist/theme-default.css';
@import '@vue-flow/node-resizer/dist/style.css';
@import '@vue-flow/controls/dist/style.css';
html,
body,
#app {
margin: 0;
height: 100%;
}
/* #app {
text-transform: uppercase;
font-family: 'JetBrains Mono', monospace;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
} */
.vue-flow__minimap {
transform: scale(75%);
transform-origin: bottom right;
}
.dnd-flow {
flex-direction: column;
display: flex;
height: 100%
}
.dnd-flow aside {
color: #fff;
font-weight: 700;
border-right: 1px solid #eee;
padding: 15px 10px;
font-size: 12px;
background: #10b981bf;
-webkit-box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, .3);
box-shadow: 0 5px 10px #0000004d
}
.dnd-flow aside .nodes>* {
margin-bottom: 10px;
cursor: grab;
font-weight: 500;
-webkit-box-shadow: 5px 5px 10px 2px rgba(0, 0, 0, .25);
box-shadow: 5px 5px 10px 2px #00000040
}
.dnd-flow aside .description {
margin-bottom: 10px
}
.dnd-flow .vue-flow-wrapper {
flex-grow: 1;
height: 100%
}
@media screen and (min-width: 640px) {
.dnd-flow {
flex-direction: row
}
.dnd-flow aside {
min-width: 25%
}
}
@media screen and (max-width: 639px) {
.dnd-flow aside .nodes {
display: flex;
flex-direction: row;
gap: 5px
}
}
.dropzone-background {
position: relative;
height: 100%;
width: 100%
}
.dropzone-background .overlay {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
pointer-events: none
}
</style>
\ No newline at end of file
......@@ -10,17 +10,21 @@
</div>
</Upload>
<div>
<Select filterable v-model="project" style="width:200px" placeholder="项目">
<Select filterable v-model="project" style="width:200px" placeholder="项目" @on-change="getview">
<Option v-for="item in projects" :value="item.id" :key="item.id">{{
item.name }}</Option>
item.name }}</Option>
</Select>
<Select filterable v-model="views" style="width:200px" placeholder="面板">
<Option v-for="item in views" :value="item.id" :key="item.id">{{
item.name }}</Option>
</Select>
<Button type="primary" @click="upload()" style="margin-left: 10px;">上传</Button>
</div>
<div style="margin-top: 10px;">
<Row v-for="item in uploadhistory">
<Col span="8" offset="1">{{ item.filename + "(" + item.project + ")" + (item.case_count
? (" -- " + item.case_count +
"条") : "") }}
? (" -- " + item.case_count +
"条") : "") }}
</Col>
<Col span="12">
<Steps :current="item.step"
......@@ -38,6 +42,7 @@
</template>
<script>
export default {
props: ['isCollapsed',],
data() {
......@@ -46,7 +51,9 @@ export default {
title: "上传用例",
file: null,
project: null,
view: null,
projects: [],
views: [],
uploadhistory: [],
runupload: [],
total: 0,
......@@ -69,8 +76,10 @@ export default {
const formData = new FormData();
if (!this.file) return this.$Message.error("请上传xmind用例文件");
if (!this.project) return this.$Message.error("请选择项目");
if (!this.view) return this.$Message.error("请选择面板");
formData.append('file', this.file);
formData.append("project", this.project);
formData.append("view", this.view);
formData.append("projectName", this.projects[this.projects.findIndex(ele => { return ele.id == this.project })].name);
this.$http.post('/tool/testcase/upload', formData, {
headers: {
......@@ -86,6 +95,9 @@ export default {
getprojects() {
this.$request('get', '/tool/testport/getproject', null, this, (data) => { this.projects = data.data })
},
getview(project) {
this.$request('get', 'tool/testport/view/' + project, null, this, (data) => { this.views = data.data })
},
getuploadhistory() {
this.$request('get', '/tool/testcase/uploadhistory', this.pageinfo, this, (data) => { this.uploadhistory = data.data.rows; this.total = data.data.total })
}
......@@ -115,6 +127,13 @@ export default {
this.runupload = a
}
},
project:{
Handle(value){
if (this.view){
this.view=null
}
}
},
runupload(newvalue, oldvalue) {
newvalue.forEach(ele => {
if (!oldvalue.includes(ele)) this.$socket.emit("join", ele)
......
<script setup>
import { computed } from 'vue'
import { BaseEdge, EdgeLabelRenderer, getBezierPath, useVueFlow } from '@vue-flow/core'
const props = defineProps()
const { removeEdges } = useVueFlow()
const path = computed(() => getBezierPath(props))
</script>
<script>
export default {
inheritAttrs: false,
}
</script>
<template>
<BaseEdge :path="path[0]" />
<EdgeLabelRenderer>
<div
:style="{
pointerEvents: 'all',
position: 'absolute',
transform: `translate(-50%, -50%) translate(${path[1]}px,${path[2]}px)`,
}"
class="nodrag nopan"
>
<button class="edgebutton" @click="removeEdges(id)">×</button>
</div>
</EdgeLabelRenderer>
</template>
<style>
.edgebutton {
border-radius: 999px;
cursor: pointer;
}
.edgebutton:hover {
box-shadow: 0 0 0 2px pink, 0 0 0 4px #f05f75;
}
</style>
<script setup>
import { Handle, Position } from '@vue-flow/core'
import { ref } from 'vue'
const counter = ref(0)
</script>
<template>
<div class="custom-node">
<Handle type="target" :position="Position.Top" />
<button class="increment nodrag" @click="counter++">遥远的东方</button>
<div v-if="counter > 0" class="counter">
<div class="count" v-for="count of counter" :key="`count-${count}`">{{ count }}</div>
</div>
</div>
</template>
<style>
.custom-node {
min-width: 100px;
gap: 4px;
padding: 8px;
background: white;
border: 1px solid black;
border-radius: 4px;
}
.increment {
border-radius: 4px;
background: #42b983;
font-size: 10px;
color: #fff;
cursor: pointer;
border: none;
}
.increment:hover {
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
.counter {
margin-top: 8px;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 4px;
}
.count {
font-size: 6px;
color: #ff0072;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 8px;
}
</style>
<script setup>
import { useVueFlow } from '@vue-flow/core'
const props = defineProps({
id: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
},
})
const { updateNodeData } = useVueFlow()
function onSelect(color) {
updateNodeData(props.id, { color, isGradient: false })
const connectedEdges = getConnectedEdges(props.id)
for (const edge of connectedEdges) {
edge.style = {
stroke: color,
}
}
}
</script>
<template>
<div>接口哈哈哈</div>
</template>
\ No newline at end of file
<script setup>
import { useVueFlow,Handle,Position } from '@vue-flow/core'
const props = defineProps({
id: {
type: String,
required: true,
},
data: {
type: Object,
required: true,
},
})
const { updateNodeData } = useVueFlow()
function onSelect(color) {
updateNodeData(props.id, { color, isGradient: false })
const connectedEdges = getConnectedEdges(props.id)
for (const edge of connectedEdges) {
edge.style = {
stroke: color,
}
}
}
</script>
<template>
<div>接口哈哈哈</div>
<Handle type="target" :position="Position.Top" />
<Handle type="source" :position="Position.Bottom" />
</template>
\ No newline at end of file
......@@ -12,6 +12,7 @@ module.exports = defineConfig({
}
},
devServer: {
allowedHosts: "all",
proxy: {
'/api': {
target: 'http://localhost:8080',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment