新增设计需求、测试项、用例批量新增页面和功能

This commit is contained in:
2025-12-19 15:56:58 +08:00
parent a0dc6bd074
commit 3bb7e027a6
52 changed files with 1852 additions and 711 deletions

View File

@@ -4,8 +4,11 @@ import MaInfo from "@/components/ma-info/index.vue"
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 tool from "@/utils/tool"
import { useRoute } from "vue-router"
export default function useKeyToMaInfo() {
const route = useRoute()
// 初始状态为空
let maInfoDom = ref(<Empty></Empty>)
// 3个列信息
@@ -17,6 +20,14 @@ export default function useKeyToMaInfo() {
})
const designColumns = useDesignColumn(undefined)
const demandColumns = useDemandColumn(undefined)
// 如果是FPGA则测试项ma-info去掉“测试项描述列”
const demandColumnsNew = computed(() => {
if (tool.checkForCpuOrFPGA(route.query.plant_type)) {
return demandColumns.value
}
return demandColumns.value.filter((item) => item.dataIndex !== "testDesciption")
})
// 函数传入switch后的Promise以及是什么类型展示信息
const fetchNodeDataAndSetMaInfo = async (resPromise: Promise<any>, nodeType: string) => {
const res = await resPromise
@@ -42,7 +53,7 @@ export default function useKeyToMaInfo() {
maInfoDom.value = <MaInfo columns={designColumns.value} data={res.data} tableLayout="auto"></MaInfo>
break
case "demand":
maInfoDom.value = <MaInfo columns={demandColumns.value} data={res.data} tableLayout="auto"></MaInfo>
maInfoDom.value = <MaInfo columns={demandColumnsNew.value} data={res.data} tableLayout="auto"></MaInfo>
break
default:
break

View File

@@ -0,0 +1,22 @@
import { CaseUserType } from "./types"
export const initCaseData: CaseUserType = {
parent_key: "",
name: "",
initialization: "软件正常启动,正常运行",
premise: "软件正常启动,外部接口运行正常",
summarize: "", // 用例综述
test_step: "", // 综合字符串
// FPGA
sequence: "" // 时序图
}
export const validationRules = {
parent_key: [{ required: true, message: "必须选择归属需求" }],
name: [{ required: true, message: "用例名称必填" }],
initialization: [],
premise: [],
summarize: [],
test_step: [{ required: true, message: "字符串录入信息必填" }],
sequence: []
}

View File

@@ -0,0 +1,50 @@
import demandApi from "@/api/project/testDemand"
import { Message } from "@arco-design/web-vue"
import { computed, ref } from "vue"
import { useRoute } from "vue-router"
interface OriginHouType {
label: string
value: number
children: {
label: string
value: number
key: string
}[]
}
export default function () {
const route = useRoute()
const casecadeLoading = ref(false)
const originOptions = ref<OriginHouType[]>([])
const casecadeOptions = computed(() => {
return originOptions.value.map(({ children, ...rest }) => ({
children: children.map(({ label, key }) => ({
isLeaf: true,
label,
value: key
})),
...rest
}))
})
// 初始化时候直接获取数据
const fetchOptionsData = async () => {
casecadeLoading.value = true
try {
// 获取轮次key
const rawKey = Array.isArray(route.query.key) ? route.query.key[0] : route.query.key
const round_key = rawKey?.split("-")[0]
const res = await demandApi.getRelatedTestDemand({
id: route.query.id,
round: round_key
})
originOptions.value = res.data
} catch (e) {
console.log(e)
Message.error("级联数据获取失败,请重新打开此页面!")
} finally {
casecadeLoading.value = false
}
}
return { fetchOptionsData, casecadeOptions }
}

View File

@@ -0,0 +1,212 @@
<template>
<a-modal fullscreen unmount-on-close v-model:visible="visible" title="批量新增测试用例" ok-text="批量新增" :on-before-ok="handleSubmit">
<a-form ref="formRef" :model="formData" layout="vertical">
<a-table
:data="formData"
:columns="filterCaseColumns"
:pagination="false"
:hoverable="false"
:scroll="isFPGA ? { x: 3000, y: 600 } : { x: 2500, y: 600 }"
:bordered="{ wrapper: true, cell: true }"
:draggable="{ type: 'handle', width: 40 }"
@change="handleDraggleChange"
>
<template #xuhao="{ rowIndex }">
{{ rowIndex + 1 }}
</template>
<template #parent_key="{ rowIndex }">
<a-form-item help="归属当前轮次的测试项" :field="`${rowIndex}.parent_key`" :rules="validationRules.parent_key" hide-label>
<a-cascader
:style="{ width: '80%' }"
allow-search
allow-clear
:options="casecadeOptions"
placeholder="请选择归属的测试项"
v-model="formData[rowIndex].parent_key"
/>
<a-button type="primary" :style="{ width: '20%' }" :disabled="rowIndex < 1" @click="handleTongshang($event, rowIndex, 'parent_key')">
同上
</a-button>
</a-form-item>
</template>
<template #name="{ rowIndex }">
<a-form-item help="填写用例名称" :field="`${rowIndex}.name`" :rules="validationRules.name" hide-label>
<a-input allow-clear v-model="formData[rowIndex].name"></a-input>
</a-form-item>
</template>
<template #initialization="{ rowIndex }">
<a-form-item help="请填写用例初始化" :field="`${rowIndex}.initialization`" hide-label>
<a-textarea allow-clear auto-size v-model="formData[rowIndex].initialization"></a-textarea>
<a-button
type="primary"
:style="{ width: '20%' }"
:disabled="rowIndex < 1"
@click="handleTongshang($event, rowIndex, 'initialization')"
>
同上
</a-button>
</a-form-item>
</template>
<template #premise="{ rowIndex }">
<a-form-item help="用例的前提与约束" :field="`${rowIndex}.premise`" hide-label>
<a-textarea allow-clear auto-size v-model="formData[rowIndex].premise"></a-textarea>
<a-button type="primary" :style="{ width: '20%' }" :disabled="rowIndex < 1" @click="handleTongshang($event, rowIndex, 'premise')">
同上
</a-button>
</a-form-item>
</template>
<template #summarize="{ rowIndex }">
<a-form-item help="填写用例综述" :field="`${rowIndex}.summarize`" hide-label>
<a-textarea allow-clear auto-size v-model="formData[rowIndex].summarize"></a-textarea>
<a-button type="primary" :style="{ width: '20%' }" :disabled="rowIndex < 1" @click="handleTongshang($event, rowIndex, 'summarize')">
同上
</a-button>
</a-form-item>
</template>
<template #sequence="{ rowIndex }">
<a-form-item :field="`${rowIndex}.sequence`" hide-label>
<ma-editor v-model="formData[rowIndex].sequence" style="width: 100%" :id="'sequence' + rowIndex"></ma-editor>
</a-form-item>
</template>
<template #test_step="{ rowIndex }">
<a-form-item :field="`${rowIndex}.test_step`" hide-label :rules="validationRules.test_step">
<a-textarea allow-clear auto-size v-model="formData[rowIndex].test_step"></a-textarea>
<PopupYonghu type="case" />
</a-form-item>
</template>
<template #caozuo="{ rowIndex }">
<a-space>
<a-button type="text" @click="handlePlusIcon">
<template #icon>
<span class="icon"><icon-plus-circle /></span>
</template>
</a-button>
<template v-if="!isLastOne">
<a-button type="text" status="danger" @click="handleDelete(rowIndex)">
<template #icon>
<span class="icon"><icon-delete /></span>
</template>
</a-button>
</template>
</a-space>
</template>
</a-table>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import { computed, nextTick, ref } from "vue"
import { initCaseData, validationRules } from "./consts"
import { CasesSubmitType, CaseUserType } from "./types"
import { Message, type FormInstance, type TableColumnData } from "@arco-design/web-vue"
import { useRoute } from "vue-router"
import { useTreeDataStore } from "@/store"
import tool from "@/utils/tool"
import useColumn from "./useColumn"
import useCasecadeOptions from "./hooks/useCasecadeOptions"
import PopupYonghu from "../BatchDemandCreate/components/YongHuTs"
import caseApi from "@/api/project/case"
const visible = ref(false)
const route = useRoute()
const treeDataStore = useTreeDataStore()
const isFPGA = ref(tool.checkForCpuOrFPGA(route.query.plant_type))
const emit = defineEmits(["batchCaseCreateComplete"])
// columns
const { caseColumns } = useColumn()
const filterCaseColumns = computed(() => {
return caseColumns.value.filter((col: TableColumnData) => {
return !(isFPGA.value === false && col.dataIndex === "sequence")
})
})
// 拖拽处理
const handleDraggleChange = (_data: any) => {
formData.value = _data // 拖拽处理
}
// 表单和表格数据
const formRef = ref<FormInstance>()
const formData = ref<CaseUserType[]>([{ ...initCaseData }])
const isLastOne = computed(() => formData.value.length <= 1)
// hooks-提前获取信息
const { fetchOptionsData, casecadeOptions } = useCasecadeOptions() // 获取当前轮次归属测试项
const open = async () => {
visible.value = true
await nextTick()
fetchOptionsData()
}
// 处理“同上”按钮
const handleTongshang = (_: MouseEvent, index: number, key: string) => {
if (Array.isArray(formData.value[index - 1][key])) {
formData.value[index][key] = [...formData.value[index - 1][key]]
return
}
formData.value[index][key] = formData.value[index - 1][key]
}
// 按钮函数
const handlePlusIcon = () => {
formData.value.push({ ...initCaseData })
}
const handleDelete = (index: number) => {
formData.value.splice(index, 1)
}
const handleDeleteAll = () => {
formData.value = [{ ...initCaseData }]
}
defineExpose({ open })
// 弹窗提交表单
const handleSubmit = async () => {
const validation = await formRef.value?.validate()
if (validation === undefined) {
// 验证通过-组装POST
const submitData: CasesSubmitType = {
project_id: route.query.id as unknown as number,
cases: formData.value
}
try {
const res = await caseApi.batchSave({ ...submitData })
if (res.code === 60000) {
// 标识录入成功了
handleDeleteAll()
res.data.forEach((it: any) => {
treeDataStore.updateTestDemandTreeData({ key: it }, route.query.id)
})
emit("batchCaseCreateComplete")
Message.success("批量新增用例成功!")
return true
} else {
if (res.code === 60001) {
// 有字符但没有@情况
Message.error(res.message)
} else if (res.code === 60002) {
// 操作输入为空,即@前面为空
Message.error(res.message)
} else if (res.code === 60003) {
// 预期为空,即@后面为空
Message.error(res.message)
} else {
Message.error("测试步骤快捷字符串录入错误,请检查!")
}
}
return false
} catch (e) {
console.log("录入用例时后前端报错,报错信息如下:", e)
return false
}
}
return false
}
</script>
<style scoped>
.icon {
font-size: 20px;
}
</style>

View File

@@ -0,0 +1,15 @@
export interface CaseUserType {
parent_key: string // 关键给后端一个测试项的key
name: string
initialization?: string // 软件正常启动,正常运行
premise?: string // 软件正常启动,外部接口运行正常
summarize?: string // 综述,取测试项?
test_step: string // 给后端传字符串,后端解析
// 后面是时序图
sequence?: string // 时序图Base64-FPGA
}
export interface CasesSubmitType {
project_id: number
cases: CaseUserType[]
}

View File

@@ -0,0 +1,73 @@
import { TableColumnData } from "@arco-design/web-vue"
import { ref } from "vue"
export default function () {
const caseColumns = ref<TableColumnData[]>([
{
title: "序号",
align: "center",
dataIndex: "xuhao",
slotName: "xuhao",
width: 50
},
{
title: "所属测试项(必填)",
dataIndex: "parent_key",
align: "center",
slotName: "parent_key",
width: 300
},
{
title: "名称(必填)",
dataIndex: "name",
align: "center",
slotName: "name",
width: 250
},
{
title: "用例初始化",
dataIndex: "initialization",
align: "center",
slotName: "initialization",
width: 300
},
{
title: "前提和约束",
dataIndex: "premise",
align: "center",
slotName: "premise",
width: 350
},
{
title: "用例综述",
dataIndex: "summarize",
align: "center",
slotName: "summarize",
width: 450
},
{
title: "测试步骤-快捷步骤录入字符串(必填)",
dataIndex: "test_step",
align: "center",
slotName: "test_step"
},
{
title: "时序图",
dataIndex: "sequence",
align: "center",
slotName: "sequence",
width: 500
},
{
title: "操作",
dataIndex: "caozuo",
align: "center",
slotName: "caozuo",
width: 250,
fixed: "right"
}
])
return { caseColumns }
}

View File

@@ -0,0 +1,60 @@
import styles from "./styles.module.less"
const PopupYonghu = (props: { type: "case" | "demand" }) => {
const content =
props.type === "demand" ? (
<a-space direction="vertical">
<p>使^@</p>
<p>
1. <a-tag color="red">^</a-tag>
</p>
<p>
2. <a-tag color="red">@</a-tag>
</p>
<p>
3. <a-tag color="red">@</a-tag>CPU
</p>
<p>
4. <a-tag color="red">@</a-tag>
</p>
<p></p>
<p>
<a-tag color="red">^</a-tag><a-tag color="red">@</a-tag>(CPU)
</p>
<p>
<a-tag color="red">@</a-tag>
</p>
<p></p>
<p>^1@测试子项1号描述</p>
<p>1@预期1号</p>
<p>2@预期2号</p>
<p>3@预期3号</p>
<p>^2@测试子项2号描述</p>
<p>1@预期1号</p>
</a-space>
) : (
<a-space direction="vertical">
<p>使@</p>
<p>1. </p>
<p>
2. <a-tag color="red">@</a-tag>
</p>
<p></p>
<p>1@步骤1的预期结果</p>
<p>2@步骤2的预期结果</p>
<p>3@步骤3的预期结果</p>
<p>4@步骤4的预期结果</p>
<p>5@步骤5的预期结果</p>
</a-space>
)
return (
<a-popover title="录入规则" position="left" key={props.type}>
{{
default: () => <icon-question-circle-fill class={styles.suffix} />,
content: () => content
}}
</a-popover>
)
}
export default PopupYonghu

View File

@@ -0,0 +1,12 @@
.suffix {
margin-left: 10px;
color: #86909c;
cursor: pointer;
font-size: 20px;
transition: all 0.3s;
color: rgb(237, 192, 68);
}
.suffix:hover {
color: rgb(var(--primary-5));
}

View File

@@ -0,0 +1,32 @@
import { demandOneType } from "./type"
// 初始化数据
export const lineInitialData: demandOneType = {
parent_key: "",
ident: "",
name: "",
priority: "1",
adequacy: "测试用例覆盖测试子项要求的全部内容。\a所有用例执行完毕对于未执行的用例说明未执行原因。",
testContent: "",
testMethod: ["4"],
testType: "4",
testDesciption: ""
}
export const validationRules = {
parent_key: [{ required: true, message: "必须选择归属需求" }],
ident: [{ required: true, message: "标识不能为空" }],
name: [{ required: true, message: "名称不能为空" }],
priority: [],
adequacy: [{ required: true, message: "充分性不能为空" }],
testMethod: [],
testType: [{ required: true, message: "请选择测试类型" }],
testDesciption: [],
testContent: [{ required: true, message: "测试子项不能为空" }]
}
export const priorityOptions = [
{ label: "高", value: "1" },
{ label: "中", value: "2" },
{ label: "低", value: "3" }
]

View File

@@ -0,0 +1,51 @@
import designApi from "@/api/project/designDemand"
import { Message } from "@arco-design/web-vue"
import { computed, ref } from "vue"
import { useRoute } from "vue-router"
// 定义后端返回的级联数据,注意修改后端也要修改此处
interface OriginOptionsType {
label: string
value: number
children: {
label: string
value: number
children: {
label: string
value: number
key: string // 这里要放options的value里面
}[]
}[]
}
export default function () {
const route = useRoute()
const casecadeLoading = ref(false)
const originOptions = ref<OriginOptionsType[]>([])
const casecadeOptions = computed(() => {
return originOptions.value.map(({ children, ...rest }) => ({
children: children.map(({ children, ...rest2 }) => ({
children: children.map(({ label, key }) => ({
label,
value: key,
isLeaf: true
})),
...rest2
})),
...rest
}))
})
// 初始化是直接获取数据
const fetchOptionsData = async () => {
casecadeLoading.value = true
try {
const res = await designApi.getRelatedCasDesign({ id: route.query.id })
originOptions.value = res.data
} catch (e) {
Message.error("级联数据获取失败,请重新打开此页面!")
} finally {
casecadeLoading.value = false
}
}
return { fetchOptionsData, casecadeOptions }
}

View File

@@ -0,0 +1,27 @@
import { computed, ref } from "vue"
import dictApi from "@/api/system/dict"
import { Message } from "@arco-design/web-vue"
export default function () {
const loading = ref(false)
const originOption = ref([])
const testMethodOptions = computed(() => {
return originOption.value.map(({ title, key }) => ({
label: title,
value: key
}))
})
// 请求字典中测试手段数据
const fetchDictData = async () => {
try {
const res = await dictApi.getDictByCode({ code: "testMethod" })
originOption.value = res.data
loading.value = true
} catch (e) {
Message.error("获取测试手段选项失败,请关闭后重新打开!")
} finally {
loading.value = false
}
}
return { fetchDictData, loading, testMethodOptions }
}

View File

@@ -0,0 +1,27 @@
import { computed, ref } from "vue"
import dictApi from "@/api/system/dict"
import { Message } from "@arco-design/web-vue"
export default function () {
const loading = ref(false)
const originOption = ref([])
const testTypeOptions = computed(() => {
return originOption.value.map(({ title, key }) => ({
label: title,
value: key
}))
})
// 请求字典中测试手段数据
const fetchTestTypeDictData = async () => {
try {
const res = await dictApi.getDictByCode({ code: "testType" })
originOption.value = res.data
loading.value = true
} catch (e) {
Message.error("获取测试手段选项失败,请关闭后重新打开!")
} finally {
loading.value = false
}
}
return { fetchTestTypeDictData, loading, testTypeOptions }
}

View File

@@ -0,0 +1,222 @@
<template>
<a-modal fullscreen unmount-on-close v-model:visible="visible" title="批量新增测试项" ok-text="批量新增" :on-before-ok="handleSubmit">
<a-form ref="formRef" :model="formData" layout="vertical">
<a-table
:data="formData"
:columns="filterDemandColumns"
:pagination="false"
:hoverable="false"
:scroll="{ x: 2500, y: 600 }"
:bordered="{ wrapper: true, cell: true }"
:draggable="{ type: 'handle', width: 40 }"
@change="handleDraggleChange"
>
<template #xuhao="{ rowIndex }">
{{ rowIndex + 1 }}
</template>
<template #parent_key="{ rowIndex }">
<a-form-item help="测试所归属的需求" :field="`${rowIndex}.parent_key`" :rules="validationRules.parent_key" hide-label>
<a-cascader
:style="{ width: '80%' }"
allow-search
allow-clear
:options="casecadeOptions"
placeholder="请选择归属的设计需求"
v-model="formData[rowIndex].parent_key"
/>
<a-button type="primary" :style="{ width: '20%' }" :disabled="rowIndex < 1" @click="handleTongshang($event, rowIndex, 'parent_key')">
同上
</a-button>
</a-form-item>
</template>
<template #ident="{ rowIndex }">
<a-form-item help="测试项标识" :field="`${rowIndex}.ident`" :rules="validationRules.ident" hide-label>
<a-input allow-clear v-model="formData[rowIndex].ident"></a-input>
</a-form-item>
</template>
<template #name="{ rowIndex }">
<a-form-item help="测试项名称" :field="`${rowIndex}.name`" :rules="validationRules.name" hide-label>
<a-input allow-clear v-model="formData[rowIndex].name"></a-input>
</a-form-item>
</template>
<template #priority="{ rowIndex }">
<a-form-item help="选择优先级" :field="`${rowIndex}.priority`" :rules="validationRules.priority" hide-label>
<a-select :options="priorityOptions" v-model="formData[rowIndex].priority"></a-select>
</a-form-item>
</template>
<template #adequacy="{ rowIndex }">
<a-form-item help="充分性描述,\a表示word换行" :field="`${rowIndex}.adequacy`" :rules="validationRules.adequacy" hide-label>
<a-textarea allow-clear auto-size v-model="formData[rowIndex].adequacy"></a-textarea>
</a-form-item>
</template>
<template #testMethod="{ rowIndex }">
<a-form-item help="选择测试手段" :field="`${rowIndex}.testMethod`" :rules="validationRules.testMethod" hide-label>
<a-select multiple :options="testMethodOptions" v-model="formData[rowIndex].testMethod"></a-select>
<a-button type="primary" :style="{ width: '20%' }" :disabled="rowIndex < 1" @click="handleTongshang($event, rowIndex, 'testMethod')">
同上
</a-button>
</a-form-item>
</template>
<template #testType="{ rowIndex }">
<a-form-item help="选择测试类型" :field="`${rowIndex}.testType`" :rules="validationRules.testType" hide-label>
<a-select allow-search :options="testTypeOptions" v-model="formData[rowIndex].testType"></a-select>
<a-button type="primary" :style="{ width: '20%' }" :disabled="rowIndex < 1" @click="handleTongshang($event, rowIndex, 'testType')">
同上
</a-button>
</a-form-item>
</template>
<template #testDesciption="{ rowIndex }">
<a-form-item help="填写测试项描述" :field="`${rowIndex}.testDesciption`" :rules="validationRules.testDesciption" hide-label>
<a-textarea allow-clear auto-size v-model="formData[rowIndex].testDesciption"></a-textarea>
</a-form-item>
</template>
<template #testContent="{ rowIndex }">
<a-form-item :field="`${rowIndex}.testContent`" hide-label :rules="validationRules.testContent">
<a-textarea allow-clear auto-size v-model="formData[rowIndex].testContent"></a-textarea>
<YonghuTs type="demand" />
</a-form-item>
</template>
<template #caozuo="{ rowIndex }">
<a-space>
<a-button type="text" @click="handlePlusIcon">
<template #icon>
<span class="icon"><icon-plus-circle /></span>
</template>
</a-button>
<template v-if="!isLastOne">
<a-button type="text" status="danger" @click="handleDelete(rowIndex)">
<template #icon>
<span class="icon"><icon-delete /></span>
</template>
</a-button>
</template>
</a-space>
</template>
</a-table>
</a-form>
</a-modal>
</template>
<script setup lang="ts">
import { computed, nextTick, ref } from "vue"
import type { demandBatchType, demandOneType } from "./type"
import { Message, type FormInstance, type TableColumnData } from "@arco-design/web-vue"
import { lineInitialData, validationRules, priorityOptions } from "./consts"
import { useRoute } from "vue-router"
import { useTreeDataStore } from "@/store"
import useColumns from "./useColumn"
import useCasecadeOptions from "./hooks/useCasecadeOptions"
import useTestMethodOptions from "./hooks/useTestMethodOptions"
import useTestTypeOptions from "./hooks/useTestTypeOptions"
import tool from "@/utils/tool"
import YonghuTs from "./components/YongHuTs"
import demandApi from "@/api/project/testDemand"
// outInit
const route = useRoute()
const treeDataStore = useTreeDataStore()
const isFPGA = ref(tool.checkForCpuOrFPGA(route.query.plant_type))
const emit = defineEmits(["batchDemandCreateComplete"])
// 表格数据-动态绑定
const formRef = ref<FormInstance>()
const formData = ref<demandOneType[]>([{ ...lineInitialData }])
const isLastOne = computed(() => formData.value.length <= 1)
// columns-根据isFPGA计算属性
const { demandColumns } = useColumns()
const filterDemandColumns = computed(() => {
return demandColumns.value.filter((col: TableColumnData) => {
return !(isFPGA.value === false && col.dataIndex === "testDesciption")
})
})
// 初始化级联下拉框数据
const { fetchOptionsData, casecadeOptions } = useCasecadeOptions()
// 初始化测试手段数据
const { fetchDictData, testMethodOptions } = useTestMethodOptions()
// 初始化测试类型数据
const { fetchTestTypeDictData, testTypeOptions } = useTestTypeOptions()
const visible = ref(false)
const open = async () => {
visible.value = true
await nextTick()
fetchOptionsData() // 打开时获取级联下拉框数据
fetchDictData() // 打开时获取测试手段数据
fetchTestTypeDictData() // 打开时获取测试类型数据
}
// 操作行
const handlePlusIcon = () => {
formData.value.push({ ...lineInitialData })
}
const handleDelete = (index: number) => {
formData.value.splice(index, 1)
}
const handleDeleteAll = () => {
formData.value = [{ ...lineInitialData }]
}
const handleTongshang = (_: MouseEvent, index: number, key: string) => {
if (Array.isArray(formData.value[index - 1][key])) {
formData.value[index][key] = [...formData.value[index - 1][key]]
return
}
formData.value[index][key] = formData.value[index - 1][key]
}
const handleDraggleChange = (_data: any) => {
formData.value = _data // 拖拽处理
}
// 异步提交按钮
const handleSubmit = async () => {
// 首先Form校验
const validation = await formRef.value?.validate()
if (validation === undefined) {
const submitData: demandBatchType = {
project_id: route.query.id as unknown as number,
demands: formData.value
}
try {
const res = await demandApi.batchSave({ ...submitData })
// 单独接口如果res.data==='ok',则创建成功
if (res.code === 200991) {
handleDeleteAll()
res.data.forEach((it: any) => {
treeDataStore.updateDesignDemandTreeData({ key: it }, route.query.id)
})
emit("batchDemandCreateComplete")
Message.success("批量新增成功!")
return true
} else {
if (res.code === 500102) {
// 有+但没有测试项名称-后面再处理
Message.error(res.message)
} else if (res.code === 500103) {
// 有+的行解析错误
Message.error(res.message)
} else if (res.code === 500104) {
// 有-但无+的行解析错误
Message.error(res.message)
} else {
Message.error("测试子项字符串解析错误,请检查后重试!")
}
return false
}
} catch (e) {
console.log("录入事后前端报错,报错信息如下:", e)
return false
}
}
return false
}
defineExpose({ open })
</script>
<style scoped>
.icon {
font-size: 20px;
}
</style>

View File

@@ -0,0 +1,16 @@
export interface demandOneType {
parent_key: string // 设计需求的key
ident: string
name: string
priority: string
adequacy: string
testMethod?: string[] | string
testType: string
testDesciption?: string
testContent: string
}
export interface demandBatchType {
project_id: number
demands: demandOneType[]
}

View File

@@ -0,0 +1,87 @@
import { TableColumnData } from "@arco-design/web-vue"
import { ref } from "vue"
export default function () {
const demandColumns = ref<TableColumnData[]>([
{
title: "序号",
align: "center",
dataIndex: "xuhao",
slotName: "xuhao",
width: 50
},
{
title: "设计需求",
dataIndex: "parent_key",
align: "center",
slotName: "parent_key",
width: 300
},
{
title: "标识(必填)",
dataIndex: "ident",
align: "center",
slotName: "ident",
width: 150
},
{
title: "名称(必填)",
dataIndex: "name",
align: "center",
slotName: "name",
width: 250
},
{
title: "优先级",
dataIndex: "priority",
align: "center",
slotName: "priority",
width: 120
},
{
title: "充分性描述(必填)",
dataIndex: "adequacy",
align: "center",
slotName: "adequacy",
width: 300
},
{
title: "测试手段",
dataIndex: "testMethod",
align: "center",
slotName: "testMethod",
width: 210
},
{
title: "测试类型",
dataIndex: "testType",
align: "center",
slotName: "testType",
width: 200
},
{
title: "测试项描述",
dataIndex: "testDesciption",
align: "center",
slotName: "testDesciption",
width: 350
},
{
title: "测试子项(必填)",
dataIndex: "testContent",
align: "center",
slotName: "testContent"
},
{
title: "操作",
dataIndex: "caozuo",
align: "center",
slotName: "caozuo",
width: 250,
fixed: "right"
}
])
return { demandColumns }
}

View File

@@ -0,0 +1,53 @@
import { TableColumnData } from "@arco-design/web-vue"
export const designColumn: TableColumnData[] = [
{
title: "序号",
align: "center",
dataIndex: "xuhao",
slotName: "xuhao",
width: 80
},
{
title: "标识",
dataIndex: "ident",
align: "center",
slotName: "ident",
width: 150
},
{
title: "名称(必填)",
dataIndex: "name",
align: "center",
slotName: "name",
width: 200
},
{
title: "章节号(必填)",
dataIndex: "chapter",
align: "center",
slotName: "chapter",
width: 150
},
{
title: "类型(必填)",
dataIndex: "demandType",
align: "center",
slotName: "demandType",
width: 200
},
{
title: "需求描述",
dataIndex: "description",
align: "center",
slotName: "description",
},
{
title: "操作",
fixed: "right", // 固定右侧
dataIndex: "caozuo",
align: "center",
slotName: "caozuo",
width: 100
}
]

View File

@@ -0,0 +1,203 @@
import { computed, defineComponent, PropType, ref } from "vue"
import type { BatchFormData } from "./types"
import { designColumn } from "./columns"
import commonApi from "@/api/common"
import { FormInstance, Message } from "@arco-design/web-vue"
import { useRoute } from "vue-router"
import designApi from "@/api/project/designDemand"
import { useTreeDataStore } from "@/store"
// props.type类型
type CreateType = "design" | "demand" | "case"
// 常量
const typeTitle = {
design: "设计需求",
demand: "测试项",
case: "用例"
}
const BatchCreate = defineComponent({
name: "BatchCreate",
emits: ["batchCreateFinish"],
props: {
type: {
type: String as PropType<CreateType>,
default: "design"
}
},
setup({ type }, { expose, emit }) {
const visible = ref(false)
const route = useRoute()
const treeDataStore = useTreeDataStore()
// 表单的引用
const formRef = ref<FormInstance>()
// 请求需求类型-预备数据
const xqType = ref<any>([])
;(function () {
commonApi.getDict("demandType").then((res: any) => {
xqType.value = res.data
})
})()
const xqTypeOptions = computed(() => {
return xqType.value.map(({ key, title }) => ({
label: title,
value: key
}))
})
// 初始化数据-默认有一行空数据
const newData = {
ident: "",
name: "",
chapter: "",
demandType: "1",
description: ""
}
const formData = ref<BatchFormData[]>([{ ...newData }])
// 定义表单的验证规则
const validationRules = {
ident: [],
name: [{ required: true, message: "需求名称不能为空" }],
chapter: [{ required: true, message: "可为'/',不能为空" }],
demandType: [{ required: true, message: "需求类型不能为空" }],
description: []
}
const open = () => {
visible.value = true
}
// data数据操作
const handleNewLine = () => {
formData.value.push({ ...newData })
}
const handleDeleteAll = () => {
formData.value = [{ ...newData }]
}
const handleDeletePoint = (idx: number) => {
formData.value.splice(idx, 1)
}
// 提交操作-可阻止关闭模态框
const handleSubmit = async () => {
// 配置提交变量
const projectId = route.query.id
const key = route.query.key
// 修改符合后端要求的数据要求content和title
const data = formData.value.map(({ name, description, ...rest }) => ({
title: name,
content: description,
...rest
}))
const validation = await formRef.value?.validate()
if (validation === undefined) {
// 验证成功
try {
const res = await designApi.multiSave({ projectId, key, data })
handleDeleteAll()
treeDataStore.updateDesignDemandTreeData(res.data, projectId)
emit("batchCreateFinish")
Message.success("批量新增成功!")
return true
} catch (e) {
return false
}
}
return false
}
expose({ open })
return () => (
<div class="batch-create-container">
<a-modal
fullscreen
unmount-on-close
v-model:visible={visible.value}
title={"批量新增" + typeTitle[type]}
ok-text="批量新增"
on-before-ok={handleSubmit}
>
<a-space>
<a-button type="primary" style={{ marginBottom: "10px" }} onClick={handleNewLine}>
{{
icon: () => <icon-plus />,
default: () => "新增一行"
}}
</a-button>
<a-popconfirm content="您确定要重置?" onOk={handleDeleteAll}>
<a-button type="primary" status="warning" style={{ marginBottom: "10px" }}>
</a-button>
</a-popconfirm>
</a-space>
<a-form ref={formRef} model={formData.value} layout="vertical">
<a-table
data={formData.value}
columns={designColumn}
pagination={false}
hoverable={false}
scroll={{ x: 1500 }}
bordered={{ wrapper: true, cell: true }}
v-slots={{
xuhao: ({ rowIndex }) => rowIndex + 1,
ident: ({ rowIndex }) => (
<a-form-item help="例如SJGL" field={`${rowIndex}.ident`} rules={validationRules.ident} hide-label>
<a-input allow-clear v-model={formData.value[rowIndex].ident} placeholder="无须写前缀" />
</a-form-item>
),
name: ({ rowIndex }) => (
<a-form-item help="章节名称" field={`${rowIndex}.name`} rules={validationRules.name} hide-label>
<a-input allow-clear v-model={formData.value[rowIndex].name} placeholder="请填写需求名称" />
</a-form-item>
),
chapter: ({ rowIndex }) => (
<a-form-item help="可填写'/'为章节号" field={`${rowIndex}.chapter`} rules={validationRules.chapter} hide-label>
<a-input allow-clear v-model={formData.value[rowIndex].chapter} placeholder="请填写章节号" />
</a-form-item>
),
demandType: ({ rowIndex }) => (
<a-form-item help="接口需补充内容" field={`${rowIndex}.demandType`} rules={validationRules.demandType} hide-label>
<a-select v-model={formData.value[rowIndex].demandType} options={xqTypeOptions.value} />
</a-form-item>
),
description: ({ rowIndex }) => (
<a-form-item field={`${rowIndex}.description`} rules={validationRules.description} hide-label>
<ma-editor v-model={formData.value[rowIndex].description} style="width: 100%" id={"description" + rowIndex}></ma-editor>
</a-form-item>
),
caozuo: ({ rowIndex }) => (
<div>
<a-popover title="新增">
<a-button type="text" onClick={handleNewLine}>
{{
icon: () => <icon-plus />
}}
</a-button>
</a-popover>
<a-popover title="删除">
<a-button type="text" status="danger" onClick={() => handleDeletePoint(rowIndex)}>
{{
icon: () => <icon-delete />
}}
</a-button>
</a-popover>
</div>
)
}}
></a-table>
</a-form>
</a-modal>
</div>
)
}
})
export default BatchCreate

View File

@@ -0,0 +1,7 @@
export interface BatchFormData {
ident?: string
name: string
chapter?: string
demandType: string
description?: string
}

View File

@@ -1,6 +1,6 @@
<template>
<div class="ai-modal-container">
<a-modal v-model:visible="visible" width="80%" unmount-on-close draggable :footer="false">
<a-modal v-model:visible="visible" width="90%" unmount-on-close draggable :footer="false">
<template #title> AI生成测试项 </template>
<div class="flex flex-col">
<a-button type="primary" :disabled="generateLoading" @click="generateClick">{{
@@ -44,10 +44,12 @@
</a-select>
</a-input-group>
<div class="m-2 flex justify-start items-center">
<div class="label">测试项描述</div>
<div class="input flex-1">
<a-input v-model="item.demandDescription"></a-input>
</div>
<template v-if="isFPGA">
<div class="label">测试项描述</div>
<div class="input flex-1">
<a-input v-model="item.demandDescription"></a-input>
</div>
</template>
</div>
<div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
<div class="arco-table-container">
@@ -59,11 +61,19 @@
<span class="arco-table-th-title label">子项序号</span>
</span>
</th>
<th class="arco-table-th" :width="400">
<th class="arco-table-th" :width="200">
<span class="arco-table-cell arco-table-cell-align-center">
<span class="arco-table-th-title label">测试子项描述</span>
<span class="arco-table-th-title label">子项名称</span>
</span>
</th>
<template v-if="!isFPGA">
<th class="arco-table-th" :width="250">
<span class="arco-table-cell arco-table-cell-align-center">
<span class="arco-table-th-title label">测试子项描述</span>
</span>
</th>
</template>
<th class="arco-table-th" :width="800">
<span class="arco-table-cell arco-table-cell-align-center">
<span class="arco-table-th-title label">测试子项步骤</span>
@@ -84,6 +94,17 @@
<a-textarea auto-size placeholder="请填写测试子项名称" v-model="row.name"></a-textarea>
</span>
</td>
<template v-if="!isFPGA">
<td class="arco-table-td">
<span class="arco-table-cell">
<a-textarea
auto-size
placeholder="请填写测试子项描述"
v-model="row.subDescription"
></a-textarea>
</span>
</td>
</template>
<td class="arco-table-td">
<span class="arco-table-cell">
<OpeAndExpect v-model="row.subStep" />
@@ -121,7 +142,9 @@ import demandApi from "@/api/project/testDemand"
import ParentPreview from "@/views/project/ParentPreview/index.vue"
// 常量
const route = useRoute()
const indexTu = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚"
const isFPGA = tool.checkForCpuOrFPGA(route.query.plant_type)
// 初始化测试类型-一起请求处理错误
const testType = ref<any>([])
@@ -138,7 +161,7 @@ const fetchTestType = async () => {
fetchTestType()
// 初始化设计需求
const route = useRoute()
const currentKey: string = route.query.key as string
const getDesign = async () => {
try {
@@ -165,7 +188,6 @@ const generateClick = async () => {
startProgressSimulation()
// 变量给AI的问题
const question = tool.htmlToTextWithDOM(designObj.value?.description || "")
console.log("给AI的问题如下", question)
// 请求后处理结果
const res = await aiApi.getAiTestItem({ question: question, stream: false })
// 判断真实接口和开发环境接口

View File

@@ -1,7 +1,11 @@
import { ref } from "vue"
import PinYinMatch from "pinyin-match"
import tool from "@/utils/tool"
import { useRoute } from "vue-router"
export default function (crudOrFormRef: any) {
const route = useRoute()
const isFpga = tool.checkForCpuOrFPGA(route.query.plant_type)
const crudColumns = ref([
{
title: "ID",
@@ -83,8 +87,7 @@ export default function (crudOrFormRef: any) {
formType: "textarea",
maxLength: 256,
commonRules: [{ required: true, message: "充分性描述必填" }],
addDefaultValue:
"测试用例覆盖XX子项名称1、XX子项名称2、XX子项名称3子项要求的全部内容。\n所有用例执行完毕对于未执行的用例说明未执行原因。"
addDefaultValue: "测试用例覆盖XX子项名称1、XX子项名称2、XX子项名称3子项要求的全部内容。\n所有用例执行完毕对于未执行的用例说明未执行原因。"
},
{
title: "测试项描述",
@@ -92,7 +95,10 @@ export default function (crudOrFormRef: any) {
dataIndex: "testDesciption",
formType: "textarea",
maxLength: 256,
placeholder: "请填写整体测试项的描述"
placeholder: "FPGA填写-请填写整体测试项的描述",
display: isFpga,
addDisplay: isFpga,
editDisplay: isFpga
},
{
title: "测试子项",
@@ -119,6 +125,15 @@ export default function (crudOrFormRef: any) {
}
}
},
{
title: "测试项描述",
dataIndex: "subDescription",
placeholder: "非FPGA填写每个子项一句话描述",
formType:"textarea",
display: !isFpga,
addDisplay: !isFpga,
editDisplay: !isFpga
},
{
title: "操作与预期",
dataIndex: "subStep",

View File

@@ -88,9 +88,11 @@ import useRalateDemand from "./hooks/useRalateDemand"
import demandApi from "@/api/project/testDemand"
import ReplaceModel from "@/views/project/opeSets/components/DesignTable/ReplaceModal.vue"
import ReplacePriority from "@/views/project/opeSets/components/DemandTable/ReplacePriority.vue"
// inits
const route = useRoute()
const treeDataStore = useTreeDataStore()
// refs
const crudRef = ref(null)

View File

@@ -44,12 +44,7 @@ const DesignSubForm = defineComponent({
{{
title: () => <span>[]-{title.value}</span>,
default: () => (
<ma-form
ref={formRef}
v-model={formData.value}
options={options.value}
columns={columnOptions.value}
>
<ma-form ref={formRef} v-model={formData.value} options={options.value} columns={columnOptions.value}>
{{
"inputPrepend-ident": () => <span>SJ-XX-</span>
}}

View File

@@ -3,12 +3,12 @@ import tool from "@/utils/tool"
import useColumn from "../hooks/useColumns"
// 设置不同ma-form选项
export default function useOptions(formRef: any) {
export default function useOptions(formRef?: any) {
const options = ref({
showButtons: false,
labelAlign: "center"
})
const crudColumns = useColumn(formRef)
const crudColumns = useColumn()
const columnOptions = computed(() => {
return tool.renameKeyInArray(crudColumns.value, "commonRules", "rules")
})

View File

@@ -13,7 +13,7 @@
<div class="uploadContainer">
<span :style="{ marginBottom: '10px', flex: '0 1 150px' }">上传需求.docx:</span>
<a-upload
:style="{ marginBottom: '10px' }"
:style="{ marginBottom: '10px', marginLeft: '9px' }"
:limit="1"
accept=".docx"
:action="`/api/dut_upload/upload_xq_docx/?parseChapter=${parseChapter}`"
@@ -24,16 +24,15 @@
></a-upload>
</div>
<div class="flex items-center gap-3">
<span class="w-[350px]">要解析的章节名称</span>
<span class="w-87.5">要解析的章节名称</span>
<a-input placeholder="输入要解析的章节名称" v-model="parseChapter"></a-input>
<span class="w-[350px]">选择需求录入类型</span>
<span class="w-87.5">选择需求录入类型</span>
<a-select allow-search v-model="selectValue">
<a-option v-for="item in demandType" :key="item.key" :value="item.key">{{ item.title }}</a-option>
</a-select>
</div>
<a-alert :style="{ margin: '10px 0' }" type="warning">
只能上传.docx<span class="important-text">如果有visio图请替换为普通图片上传</span
>请在需求规格说明文档中操作 ->
只能上传.docx<span class="important-text">如果有visio图请替换为普通图片上传</span>请在需求规格说明文档中操作 ->
<span class="important-text">引用 -> 目录 -> 自定义目录 -> 显示级别改为6</span>以上保存后上传
</a-alert>
<div class="operation-container">
@@ -45,60 +44,26 @@
</div>
<a-spin :loading="loading" tip="解析word完成正在渲染界面..." :style="{ width: '100%' }">
<div class="demand-container">
<a-list
@page-change="handlePageChange"
:data="htmlData"
:pagination-props="{ defaultPageSize: 15, total: htmlData.length }"
>
<a-list @page-change="handlePageChange" :data="htmlData" :pagination-props="{ defaultPageSize: 15, total: htmlData.length }">
<template #item="{ item, index }">
<a-list-item>
<div class="item-container">
<a-input-group>
<a-input
placeholder="章节号"
v-model="item.chapter"
:style="{ width: '100px' }"
@click.stop.prevent
></a-input>
<a-input
placeholder="标题"
v-model="item.title"
:style="{ width: '300px' }"
@click.stop.prevent
></a-input>
<a-input
placeholder="标识"
v-model="item.ident"
:style="{ width: '200px' }"
@click.stop.prevent
></a-input>
<a-select
:style="{ width: '150px' }"
placeholder="请选择设计需求类型"
@click.stop.prevent
v-model="item.demandType"
>
<a-input placeholder="章节号" v-model="item.chapter" :style="{ width: '100px' }" @click.stop.prevent></a-input>
<a-input placeholder="标题" v-model="item.title" :style="{ width: '300px' }" @click.stop.prevent></a-input>
<a-input placeholder="标识" v-model="item.ident" :style="{ width: '200px' }" @click.stop.prevent></a-input>
<a-select :style="{ width: '150px' }" placeholder="请选择设计需求类型" @click.stop.prevent v-model="item.demandType">
<a-option v-for="it in demandType" :value="it.key">{{ it.title }}</a-option>
</a-select>
</a-input-group>
<a-button-group>
<a-button
type="primary"
status="success"
size="small"
@click.stop.prevent="handledownCreate(index)"
>
<a-button type="primary" status="success" size="small" @click.stop.prevent="handledownCreate(index)">
<template #icon>
<icon-plus />
</template>
下方新增
</a-button>
<a-button
type="primary"
status="danger"
size="small"
@click.stop.prevent="handleDelete(index)"
>
<a-button type="primary" status="danger" size="small" @click.stop.prevent="handleDelete(index)">
<template #icon>
<icon-delete />
</template>
@@ -146,8 +111,7 @@ const modalVisible = ref(false)
const htmlData = ref([])
// ~~~~1.list~~~~
const { loading, handleCreateAtLatest, handleResetData, handledownCreate, handlePageChange, handleDelete } =
useListOperaton(htmlData)
const { loading, handleCreateAtLatest, handleResetData, handledownCreate, handlePageChange, handleDelete } = useListOperaton(htmlData)
// ~~~~2.upload~~~~
const { handleUploadSuccess, handleUploadError, parseChapter, selectValue } = useUpload(htmlData)

View File

@@ -1,6 +1,6 @@
import { ref } from "vue"
export default function (crudOrFormRef: any) {
export default function (crudOrFormRef?: any) {
const crudColumns = ref([
{
title: "ID",

View File

@@ -2,30 +2,29 @@
<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
id="basic-table-design-normal"
:options="crudOptions"
:columns="crudColumns"
ref="crudRef"
:parent-key="route.query.key"
>
<ma-crud id="basic-table-design-normal" :options="crudOptions" :columns="crudColumns" ref="crudRef" :parent-key="route.query.key">
<template #ident="{ record }">
{{ showType(record) }}
</template>
<template #tableAfterButtons>
<a-space>
<a-button
status="success"
type="outline"
@click="handleAddFileInputDemand"
v-if="isXQ === 'XQ'"
>
<a-button status="success" type="outline" @click="handleAddFileInputDemand" v-if="isXQ === 'XQ'">
<template #icon>
<icon-plus />
</template>
上传需求规格说明快捷录入
</a-button>
<a-divider direction="vertical" type="double" />
<a-dropdown-button type="primary" @click="handleBatchCreate">
批量建设计需求
<template #icon>
<icon-down />
</template>
<template #content>
<a-doption @click="handleBatchDemandCreate">批量创建测试项</a-doption>
<a-doption @click="handleBatchCaseCreate">批量创建测试用例</a-doption>
</template>
</a-dropdown-button>
<a-button type="outline" @click="handleReplaceClick">批量替换</a-button>
</a-space>
</template>
@@ -49,6 +48,12 @@
popup-key="design-normal"
@replaceSuccess="replaceSuccessHandle"
/>
<!-- 批量新增设计需求组件 -->
<BatchDesginCreate ref="batchCreateRef" :typeDict="demandTypeDict" @batchCreateFinish="refreshCrudTable" />
<!-- 批量创建测试项组件 -->
<BatchDemandCreate ref="batchCreateDemandRef" @batchDemandCreateComplete="refreshCrudTable" />
<!-- 批量创建测试用例组件 -->
<BatchCaseCreate ref="batchCreateCaseRef" @batchCaseCreateComplete="refreshCrudTable" />
</div>
</template>
@@ -63,6 +68,9 @@ import designApi from "@/api/project/designDemand"
import commonApi from "@/api/common"
import FileInputModal from "./components/FileInputModal/index.vue"
import ReplaceModel from "@/views/project/opeSets/components/DesignTable/ReplaceModal.vue"
import BatchDesginCreate from "@/views/project/components/BatchDesignCreate"
import BatchDemandCreate from "@/views/project/components/BatchDemandCreate/index.vue"
import BatchCaseCreate from "@/views/project/components/BatchCaseCreate/index.vue"
const route = useRoute()
const crudRef = ref()
@@ -70,6 +78,10 @@ const projectId = ref(route.query.id)
// 5月28日新增功能替换
const replaceModal = ref()
// 12月16日新增功能批量添加
const batchCreateRef = ref()
const batchCreateDemandRef = ref()
const batchCreateCaseRef = ref()
const handleReplaceClick = () => {
replaceModal.value?.open(crudRef.value.getSelecteds) // 把获取选中行的函数给传递给替换组件
@@ -116,6 +128,21 @@ const handleAddFileInputDemand = () => {
fileInputRef.value.open()
}
// ~~~批量新增设计需求弹窗~~~
const handleBatchCreate = () => {
batchCreateRef.value.open({})
}
// ~~~批量新增测试项弹窗~~~
const handleBatchDemandCreate = () => {
batchCreateDemandRef.value.open({})
}
// ~~~批量新增测试用例弹窗~~~
const handleBatchCaseCreate = () => {
batchCreateCaseRef.value.open({})
}
const refreshCrudTable = () => {
crudRef.value.refresh()
}

View File

@@ -27,8 +27,10 @@ import useColumn from "@/views/project/dut/hooks/useColumns"
// refs
const formRef = ref(null)
// 0.props-表示不通用代码
const { designInfo } = defineProps<{ designInfo: any }>()
// 计算属性单独处理
const designInfoJudge = computed(() => {
return designInfo

View File

@@ -3,11 +3,7 @@
<search v-show="searchVisible" @submit="searchSubmit" />
<div class="lg:flex justify-between mb-2">
<a-space>
<a-popconfirm
content="确定要删除数据吗? 这会删除全部下级数据!"
position="bottom"
@ok="deletesMultipleAction"
>
<a-popconfirm content="确定要删除数据吗? 这会删除全部下级数据!" position="bottom" @ok="deletesMultipleAction">
<a-button type="primary" status="danger">
批量删除
<template #icon><icon-delete /></template>
@@ -62,11 +58,7 @@
<template v-for="column in columns" :key="column.dataIndex">
<template v-if="!column.hide">
<!-- 正常的数据列 -->
<a-table-column
v-bind="column"
v-if="!column.showType && column.dataIndex !== 'testContent'"
tooltip
>
<a-table-column v-bind="column" v-if="!column.showType && column.dataIndex !== 'testContent'" tooltip>
<!-- 如果column有isHyperText属性则直接渲染html -->
<template #cell="{ record }" v-if="column.isHyperText">
<div v-html="record[column.dataIndex]"></div>
@@ -79,15 +71,12 @@
<!-- 如果有测试子项即subStep -->
<template v-for="(sub, idx) in record[column.dataIndex]" :key="idx">
<!-- 这是每个测试子项 -->
<div class="subTitle mt-1">{{ idx + 1 }}.{{ sub.subName }}</div>
<div class="subTitle mt-3">{{ idx + 1 }}.{{ sub.subName }}</div>
<div>测试子项描述{{ sub.subDescription }}</div>
<template v-for="(step, index) in sub.subStep" :key="index">
<span class="text-amber-700">步骤{{ index + 1 }}</span>
<div class="operation">
<span class="text-bold">操作</span>{{ step.operation }}
</div>
<div class="mb-1">
<span class="text-bold">预期</span>{{ step.expect }}
</div>
<div class="operation"><span class="text-bold">操作</span>{{ step.operation }}</div>
<div class="mb-1"><span class="text-bold">预期</span>{{ step.expect }}</div>
</template>
</template>
</div>
@@ -247,10 +236,7 @@ const showType = useShowType("priority")
const showTestType = useShowType("testType")
// 3.query查询和分页相关
const { tableData, isFetching, fetchData, total, pageChange, pageSizeChange, searchParams } = useFetchData(
demandApi.getTestDemandList,
columns
)
const { tableData, isFetching, fetchData, total, pageChange, pageSizeChange, searchParams } = useFetchData(demandApi.getTestDemandList, columns)
// 4.表单相关
const formRef = ref<InstanceType<typeof Form> | null>(null)

View File

@@ -40,7 +40,7 @@ const DutSubForm = defineComponent({
// 注意v-model:visible是不能放在对象解构的
<a-modal {...modalOptions} v-model:visible={visible.value} unmount-on-close>
{{
title: () => <span>[]-{title.value}</span>,
title: () => <span >[]-{title.value}</span>,
default: () => (
<ma-form
ref={formRef}

View File

@@ -9,7 +9,7 @@ const beiceType: BeiceTypeT[] = [
{ label: "设计说明", value: "SJ" },
{ label: "需求文档", value: "XQ" },
{ label: "通信协议", value: "XY" },
{ label: "研制总要求/技术协议等", value: "YZ" }
{ label: "研制总要求/技术协议/顶层任务书等", value: "YZ" }
]
export default beiceType

View File

@@ -8,15 +8,11 @@ import useBeforeCancel from "@/views/project/projPublicHooks/useBeforeCancel"
import { cloneDeep } from "lodash-es"
const CaseSubForm = defineComponent({
name: "DemandSubFormForm",
name: "CaseSubFrom",
setup(_, { expose }) {
// hook variable
const treeDataStore = useTreeDataStore()
const { title, formData, formRef, modalOptions, project_id, visible } = subFormHooks(
caseApi.update,
treeDataStore.updateCaseTreeData,
"80%"
)
const { title, formData, formRef, modalOptions, project_id, visible } = subFormHooks(caseApi.update, treeDataStore.updateCaseTreeData, "80%")
// hooks
const { options, columnOptions } = useOptions(formRef) // **option里面变化**
// 双击打开回调
@@ -26,7 +22,7 @@ const CaseSubForm = defineComponent({
const key = nodeData.key as string
// 设置表单名称
title.value = nodeData.title!
// 注意这里因为case接口原因这里需要projectId!!!!!!!!!!!!!!!
// 注意这里因为case接口原因这里需要projectId!
const res = await caseApi.getCaseOne({ projectId: project_id, key }) // **API变化**
// 得到数据时候将beforeFormContent搞定
beforeFormContent.value = cloneDeep(res.data.testStep)
@@ -52,23 +48,10 @@ const CaseSubForm = defineComponent({
// Dom
return () => (
// 注意v-model:visible是不能放在对象解构的
<a-modal
{...modalOptions}
v-model:visible={visible.value}
on-before-cancel={handleBeforeCancel}
width="86%"
unmount-on-close
>
<a-modal {...modalOptions} v-model:visible={visible.value} on-before-cancel={handleBeforeCancel} width="86%" unmount-on-close>
{{
title: () => <span>[]-{title.value}</span>,
default: () => (
<ma-form
ref={formRef}
v-model={formData.value}
options={options.value}
columns={columnOptions.value}
></ma-form>
)
default: () => <ma-form ref={formRef} v-model={formData.value} options={options.value} columns={columnOptions.value}></ma-form>
}}
</a-modal>
)

View File

@@ -361,7 +361,7 @@ const useCrudInit = function () {
{
title: "依据标准",
dataIndex: "standard",
addDefaultValue: ["16", "2", "17", "3", "7", "4", "5", "6"],
addDefaultValue: ["16", "17", "3", "7", "4", "5", "18", "6", "19"],
maxTagCount: 20,
commonRules: [{ required: true, message: "请至少选择一个" }],
hide: true,