设计需求、测试项、用例的上级hover显示

This commit is contained in:
2025-05-14 19:57:11 +08:00
parent 25b5576274
commit 70e719abbe
15 changed files with 260 additions and 32 deletions

View File

@@ -10,7 +10,7 @@
"dependencies": {
"@arco-design/color": "^0.4.0",
"@arco-design/web-vue": "^2.57.0",
"@tanstack/vue-query": "^5.75.6",
"@tanstack/vue-query": "^5.76.0",
"@tinymce/tinymce-vue": "^6.1.0",
"@vueuse/core": "^13.1.0",
"axios": "^1.9.0",
@@ -24,10 +24,10 @@
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^3.0.2",
"pinyin-match": "^1.2.7",
"pinyin-match": "^1.2.8",
"postcss-import": "^16.1.0",
"qs": "^6.14.0",
"tailwind-merge": "^3.2.0",
"tailwind-merge": "^3.3.0",
"tinymce": "^7.8.0",
"tw-animate-css": "^1.2.9",
"vue": "^3.5.13",
@@ -1802,9 +1802,9 @@
}
},
"node_modules/@tanstack/query-core": {
"version": "5.75.6",
"resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.75.6.tgz",
"integrity": "sha512-r+WQ/z30KZF0TFkzCT7C8gWgrLuv7/t+gEjqeEp69JAIUS/omuieaHeIkoscjEcT6yaWebXGTyjdiIxJkYzEXg==",
"version": "5.76.0",
"resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.76.0.tgz",
"integrity": "sha512-FN375hb8ctzfNAlex5gHI6+WDXTNpe0nbxp/d2YJtnP+IBM6OUm7zcaoCW6T63BawGOYZBbKC0iPvr41TteNVg==",
"license": "MIT",
"funding": {
"type": "github",
@@ -1812,13 +1812,13 @@
}
},
"node_modules/@tanstack/vue-query": {
"version": "5.75.6",
"resolved": "https://registry.npmmirror.com/@tanstack/vue-query/-/vue-query-5.75.6.tgz",
"integrity": "sha512-NhE1BTyWkQmqqRGHmMj3t1vj6SvH46QlHOtOc43fS87RCfkrfkzDV6uB+nHutaPEdeIU2treLhZNzxAGNBYFvQ==",
"version": "5.76.0",
"resolved": "https://registry.npmmirror.com/@tanstack/vue-query/-/vue-query-5.76.0.tgz",
"integrity": "sha512-Ow1JPfAqjam/hH2WJj15Y+a7FT5LS/So2b4jMAI3lLKUcpIjVRLEOLQXdCYZ/29YbDrL66trwLPXjA9tYrHung==",
"license": "MIT",
"dependencies": {
"@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/query-core": "5.75.6",
"@tanstack/query-core": "5.76.0",
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10"
},
@@ -5263,9 +5263,9 @@
}
},
"node_modules/pinyin-match": {
"version": "1.2.7",
"resolved": "https://registry.npmmirror.com/pinyin-match/-/pinyin-match-1.2.7.tgz",
"integrity": "sha512-RnypoF7OKgSKL8L4IayHmmHj9cHeked45TFAOffddcvpIIDrLcTSEJNruprE5otXCegBBqWm4YEi8JwMqToeig==",
"version": "1.2.8",
"resolved": "https://registry.npmmirror.com/pinyin-match/-/pinyin-match-1.2.8.tgz",
"integrity": "sha512-5rBwNuOjnOtZNEgX4OlTLYsmBcE9XSV1oF/KN9mLEsVNr8HdqMb2YRhR6iqHMeU8ZBKbx/oYBgHr04uIvOlxGg==",
"license": "SATA"
},
"node_modules/pkce-challenge": {
@@ -6051,9 +6051,9 @@
}
},
"node_modules/tailwind-merge": {
"version": "3.2.0",
"resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.2.0.tgz",
"integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==",
"version": "3.3.0",
"resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.3.0.tgz",
"integrity": "sha512-fyW/pEfcQSiigd5SNn0nApUOxx0zB/dm6UDU/rEwc2c3sX2smWUNbapHv+QRqLGVp9GWX3THIa7MUGPo+YkDzQ==",
"license": "MIT",
"funding": {
"type": "github",

View File

@@ -13,7 +13,7 @@
"dependencies": {
"@arco-design/color": "^0.4.0",
"@arco-design/web-vue": "^2.57.0",
"@tanstack/vue-query": "^5.75.6",
"@tanstack/vue-query": "^5.76.0",
"@tinymce/tinymce-vue": "^6.1.0",
"@vueuse/core": "^13.1.0",
"axios": "^1.9.0",
@@ -27,10 +27,10 @@
"mitt": "^3.0.1",
"nprogress": "^0.2.0",
"pinia": "^3.0.2",
"pinyin-match": "^1.2.7",
"pinyin-match": "^1.2.8",
"postcss-import": "^16.1.0",
"qs": "^6.14.0",
"tailwind-merge": "^3.2.0",
"tailwind-merge": "^3.3.0",
"tinymce": "^7.8.0",
"tw-animate-css": "^1.2.9",
"vue": "^3.5.13",

View File

@@ -17,7 +17,14 @@
>
<template #title>{{ actionTitle }}</template>
<a-spin :loading="dataLoading" tip="加载中..." class="w-full">
<ma-form v-model="form" :columns="formColumns" :options="formOptions" ref="maFormRef">
<!-- 修改源码parentKey -->
<ma-form
v-model="form"
:columns="formColumns"
:parent-key="props.parentKey"
:options="formOptions"
ref="maFormRef"
>
<template v-for="slot in Object.keys($slots)" #[slot]="component">
<slot :name="slot" v-bind="component" />
</template>
@@ -35,6 +42,11 @@ import { useRouter } from "vue-router"
import tool from "@/utils/tool"
import { useFormStore } from "@/store/index"
// 2025年5月14日新增
const props = defineProps({
parentKey: { type: String, default: "" }
})
const formStore = useFormStore()
const router = useRouter()
const formOptions = ref({ showButtons: false })

View File

@@ -257,7 +257,8 @@
<ma-setting ref="crudSettingRef" @onChangeSearchHide="initSearchColumns()" @onChangeColumnHide="changeColumn" />
<!-- 修改源码透传ma-crud属性给ma-form -->
<ma-form ref="crudFormRef" @success="requestSuccess" v-bind="$attrs">
<!-- 修改源码传递parentKey值 -->
<ma-form ref="crudFormRef" @success="requestSuccess" v-bind="$attrs" :parent-key="props.parentKey">
<template v-for="(slot, index) in Object.keys($slots)" #[slot]="component" :key="index">
<slot :name="slot" v-bind="component" />
</template>
@@ -308,7 +309,9 @@ const props = defineProps({
options: { type: Object, default: {} },
crud: { type: Object, default: {} },
// 字段列设置
columns: { type: Array, default: [] }
columns: { type: Array, default: [] },
// ~~~2025年5月14日新增透传给ma-form字段
parentKey: { type: String, default: "" }
})
const loading = ref(true)

View File

@@ -61,7 +61,7 @@
</a-button>
</a-tooltip>
<a-button-group shape="round" size="mini">
<a-button type="primary" @click="moveUp(index)">
<a-button type="primary" @click="moveUp(index)">
<icon-arrow-rise />
</a-button>
<a-button type="primary" @click="moveDown(index)">

View File

@@ -63,12 +63,16 @@
</div>
</slot>
</a-form>
<!-- 修改源码判断是否传入parentKey -->
<template v-if="parentKey">
<ParentPreview :parent-key="parentKey"></ParentPreview>
</template>
</a-spin>
</div>
</template>
<script setup>
import { ref, watch, provide, onMounted, nextTick, getCurrentInstance } from "vue"
import { ref, watch, provide, onMounted, nextTick, getCurrentInstance, inject, computed } from "vue"
import { isNil, set, get, cloneDeep } from "lodash-es"
import defaultOptions from "./js/defaultOptions.js"
import {
@@ -94,11 +98,32 @@ const dictList = ref({})
const cascaderList = ref([])
const form = ref({})
// custom start
// 2025年5月14日新增功能hover查看上级节点
import ParentPreview from "@/views/project/ParentPreview/index.vue"
// 判断是否有
const formKey = computed(() => {
// 去掉双击被测件即key.split("").length > 1
if (form.value.key && form.value.key.split("-").length > 2) {
// 如果存在则取前面的
return form.value.key.slice(0, -2)
}
return ""
})
const parentKey = computed(() => {
return props.parentKey || formKey.value || ""
})
// custom end
const props = defineProps({
modelValue: { type: Object, default: {} },
columns: { type: Array },
options: { type: Object, default: {} }
options: { type: Object, default: {} },
// 2025年5月14日新增属性-非必须,后面根据非必须判断
parentKey: { type: String, default: "" }
})
const emit = defineEmits(["submit", "update:modelValue"])
watch(

View File

@@ -6,7 +6,7 @@
:title="props.title"
:layout="props.layout"
:bordered="props.bordered"
table-layout="fixed"
:table-layout="props.tableLayout"
:size="props.size"
:label-style="props.labelStyle"
:value-style="props.valueStyle"
@@ -105,7 +105,9 @@ const props = defineProps({
},
size: {
default: "large"
}
},
// 新增
tableLayout: { type: String, default: "fixed" }
})
watch(

View File

@@ -3,7 +3,7 @@ import { useDebounceFn } from '@vueuse/core'
import { useAppStore } from '@/store'
import { addEventListen, removeEventListen } from '@/utils/event'
const WIDTH = 992 // https://arco.design/vue/component/grid#responsivevalue
const WIDTH = 992
function queryDevice() {
const rect = document.body.getBoundingClientRect()

View File

@@ -0,0 +1,166 @@
<script lang="tsx">
import { computed, defineComponent, ref, Teleport } from "vue"
import { useRoute } from "vue-router"
import { Popover } from "@arco-design/web-vue"
import MaInfo from "@/components/ma-info/index.vue"
// 请求的API
import dutApi from "@/api/project/dut"
import designApi from "@/api/project/designDemand"
import demandApi from "@/api/project/testDemand"
// columns导入
import useDutColumn from "@/views/project/round/hooks/useColumn"
import useDesignColumn from "@/views/project/dut/hooks/useColumns"
import useDemandColumn from "@/views/project/design-demand/hooks/useColumns"
import Empty from "@/components/Empty/index.vue"
export default defineComponent({
name: "ParentPreview",
props: {
parentKey: { type: String, default: "" } // 在ma-form已经判断所以必然有值索引上级key
},
setup(props) {
const route = useRoute()
const project_id = route.query.id // 项目id
const hoverText = ref("")
const buttonLikeRef = ref<HTMLDivElement | null>(null)
const onMouseenter = () => {
// 进入时候注册事件
hoverText.value = "查看上级"
}
const onMouseleave = () => {
hoverText.value = ""
}
// ma-info变量
const dutOriginColumns = useDutColumn(undefined)
const dutColumns = computed(() => {
// 去掉上传源代码字段
const quUploadColumns = dutOriginColumns.value.filter((it) => it.dataIndex !== "upload")
// 判断是否为源代码被测件
return quUploadColumns
})
const designColumns = useDesignColumn(undefined)
const demandColumns = useDemandColumn(undefined)
// 根据parentKey判断是哪个级别节点
const keyLength = props.parentKey.split("-").length
// 储存ma-info的dom
let maInfoDom = <Empty></Empty>
// 处理异步请求的函数
const fetchNodeData = async (resPromise: Promise<any>, nodeType: string) => {
const res = await resPromise
switch (nodeType) {
case "dut":
const dutInfo = res.data
const dutInfoJudge = computed(() => {
if (dutInfo.type === "SO") {
// 计算注释率:注释行/总行数
dutInfo.comment_percent = (dutInfo.comment_lines / dutInfo.total_lines) * 100 + "%"
} else {
// 如果是非源代码被测件行数均填写:“不适用”
dutInfo.comment_lines = "不适用"
dutInfo.comment_percent = "不适用"
dutInfo.effective_lines = "不适用"
dutInfo.total_lines = "不适用"
}
return dutInfo
})
maInfoDom = (
<MaInfo columns={dutColumns.value} data={dutInfoJudge.value} tableLayout="auto"></MaInfo>
)
break
case "design":
maInfoDom = <MaInfo columns={designColumns.value} data={res.data} tableLayout="auto"></MaInfo>
break
case "demand":
maInfoDom = <MaInfo columns={demandColumns.value} data={res.data} tableLayout="auto"></MaInfo>
break
default:
break
}
}
switch (keyLength) {
case 2:
// 请求设计需求节点
fetchNodeData(dutApi.getDutOne({ project_id, key: props.parentKey }), "dut")
break
case 3:
// 请求测试项节点
fetchNodeData(designApi.getDesignDemandOne({ project_id, key: props.parentKey }), "design")
break
case 4:
// 请求用例节点
fetchNodeData(demandApi.getTestDemandOne({ project_id, key: props.parentKey }), "demand")
break
default:
break
}
return () => (
<Teleport to="body">
<Popover trigger="click" position="left">
{{
default: () => (
<div class="preview-container">
<div
class="button-like"
ref={buttonLikeRef}
onMouseenter={onMouseenter}
onMouseleave={onMouseleave}
>
<icon-find-replace />
{hoverText.value && <span class="ml-2">{hoverText.value}</span>}
</div>
</div>
),
content: () => (
<div
style={{
width: "600px"
}}
>
{maInfoDom}
</div>
)
}}
</Popover>
</Teleport>
)
}
})
</script>
<style scoped lang="less">
.preview-container {
position: fixed;
right: 80px;
top: 45%;
z-index: 10000;
}
.button-like {
width: 40px;
height: 40px;
font-size: 16px;
border-radius: 50%;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
user-select: none;
cursor: pointer;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.8);
transition: all 0.1s;
overflow: hidden;
position: relative;
span {
white-space: nowrap;
}
}
.button-like:hover {
width: 120px;
border-radius: 22px;
}
.click-content {
min-width: 500px;
min-height: 300px;
background-color: #fff;
}
</style>

View File

@@ -6,6 +6,7 @@ import useOptions from "./useOptions"
import subFormHooks from "@/views/project/projPublicHooks/subFormHooks"
import useBeforeCancel from "@/views/project/projPublicHooks/useBeforeCancel"
import { cloneDeep } from "lodash-es"
import ParentPreview from "@/views/project/ParentPreview/index.vue"
const DemandSubForm = defineComponent({
name: "DemandSubFormForm",

View File

@@ -60,7 +60,7 @@ export default function (crudRef: Ref<InstanceType<typeof MaCrud>>) {
crudRef.value.crudFormRef.actionTitle = `${route.query.ident} > ${td[round_key].title} > ${td[round_key].children[dut_key].title} > ${td[round_key].children[dut_key].children[design_key].title} > 测试项-`
return true
},
beforeOpenEdit: function (record) {
beforeOpenEdit: function (record: any) {
// 1.储存打开前form的content数据到ref中以便后续比较
beforeFormContent = cloneDeep(record.testContent)
// 2.处理标识

View File

@@ -2,7 +2,13 @@
<div class="ma-content-block lg:flex justify-between p-4">
<div class="lg:w-full w-full lg:ml-4 mt-5 lg:mt-0">
<!-- CRUD组件 -->
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef" @beforeCancel="handleBeforeCancel">
<ma-crud
:options="crudOptions"
:columns="crudColumns"
ref="crudRef"
@beforeCancel="handleBeforeCancel"
:parent-key="route.query.key"
>
<template #ident="{ record }">
{{ showType(record) }}
</template>
@@ -44,10 +50,13 @@
<script setup>
import { ref } from "vue"
import commonApi from "@/api/common"
import { useRoute } from "vue-router"
// hooks
import useCrudOpMore from "./hooks/useCrudOpMore"
import useColumn from "./hooks/useColumns"
import useRalateDemand from "./hooks/useRalateDemand"
// inits
const route = useRoute()
// refs
const crudRef = ref(null)
// 根据传参获取key分别为轮次、设计需求的key

View File

@@ -2,7 +2,7 @@
<div class="ma-content-block lg:flex justify-between p-4">
<div class="lg:w-full w-full lg:ml-4 mt-5 lg:mt-0">
<!-- CRUD组件 -->
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef">
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef" :parent-key="route.query.key">
<template #ident="{ record }">
{{ showType(record) }}
</template>

View File

@@ -2,7 +2,13 @@
<div class="ma-content-block lg:flex justify-between p-4">
<div class="lg:w-full w-full lg:ml-4 mt-5 lg:mt-0">
<!-- CRUD组件 -->
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef" @beforeCancel="handleBeforeCancel">
<ma-crud
:options="crudOptions"
:columns="crudColumns"
ref="crudRef"
@beforeCancel="handleBeforeCancel"
:parent-key="route.query.key"
>
<template #ident="{ record }">
{{ showType(record) }}
</template>
@@ -17,6 +23,8 @@ import { ref } from "vue"
import ProblemForm from "@/views/project/case/components/ProblemForm.vue"
import useCrudOpMore from "./hooks/useCrudOpMore"
import useColumn from "./hooks/useColumn"
import { useRoute } from "vue-router"
const route = useRoute()
const problemFormRef = ref(null)
const title = ref("问题单表单")
const crudRef = ref()

View File

@@ -6,6 +6,7 @@
"declarationMap": true,
"esModuleInterop": true,
"jsx": "preserve",
"jsxImportSource": "vue",
"allowJs": true,
"strictFunctionTypes": false,
"strictNullChecks": true,
@@ -45,6 +46,7 @@
],
"types": [
"vite/client",
"@arco-design/web-vue"
]
},
"include": [