Compare commits

...

3 Commits

Author SHA1 Message Date
dfc81af93c 实现AI生成测试项接口 2026-05-29 16:21:14 +08:00
af2b7d1909 完成3月试用问题修改2 2026-04-24 16:49:56 +08:00
29c921cc46 完成3月试用问题修改 2026-04-24 16:45:37 +08:00
15 changed files with 923 additions and 730 deletions

911
cdTMP/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "testplant",
"private": true,
"version": "0.1.1",
"version": "0.1.2",
"type": "module",
"scripts": {
"dev": "vite",
@@ -13,13 +13,13 @@
"dependencies": {
"@arco-design/color": "^0.4.0",
"@arco-design/web-vue": "^2.58.0",
"@tanstack/vue-query": "^5.99.2",
"@tanstack/vue-query": "^5.100.14",
"@tinymce/tinymce-vue": "^6.3.0",
"@vueuse/core": "^14.2.1",
"axios": "^1.15.2",
"@vueuse/core": "^14.3.0",
"axios": "^1.16.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.20",
"dayjs": "^1.11.21",
"file2md5": "^1.3.0",
"lodash-es": "^4.18.1",
"mammoth": "^1.12.0",
@@ -28,38 +28,38 @@
"pinia": "^3.0.4",
"pinyin-match": "^1.2.10",
"postcss-import": "^16.1.1",
"qs": "^6.15.1",
"tailwind-merge": "^3.5.0",
"qs": "^6.15.2",
"tailwind-merge": "^3.6.0",
"tinymce": "^7.9.1",
"tw-animate-css": "^1.4.0",
"vue": "^3.5.32",
"vue": "^3.5.35",
"vue-clipboard3": "^2.0.0",
"vue-color-kit": "^1.0.6",
"vue-data-ui": "^3.17.13",
"vue-router": "^5.0.4",
"vue-data-ui": "^3.20.11",
"vue-router": "^5.1.0",
"vuedraggable": "^2.24.3"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.2.4",
"@tailwindcss/vite": "^4.2.4",
"@tailwindcss/postcss": "^4.3.0",
"@tailwindcss/vite": "^4.3.0",
"@types/lodash-es": "^4.17.12",
"@types/node": "^25.6.0",
"@types/node": "^25.9.1",
"@types/nprogress": "^0.2.3",
"@types/qs": "^6.15.0",
"@vitejs/plugin-vue": "^6.0.6",
"@types/qs": "^6.15.1",
"@vitejs/plugin-vue": "^6.0.7",
"@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/babel-plugin-jsx": "^2.0.1",
"browserslist": "^4.28.2",
"eslint": "^10.2.1",
"eslint-plugin-vue": "^10.9.0",
"eslint": "^10.4.0",
"eslint-plugin-vue": "^10.9.1",
"less": "^4.6.4",
"less-loader": "^12.3.2",
"postcss": "^8.5.10",
"less-loader": "^13.0.0",
"postcss": "^8.5.15",
"prettier": "^3.8.3",
"rollup-plugin-visualizer": "^7.0.1",
"tailwindcss": "^4.2.4",
"tailwindcss": "^4.3.0",
"typescript": "^6.0.3",
"vite": "^8.0.9",
"vite": "^8.0.14",
"vue-eslint-parser": "^10.4.0"
}
}

View File

@@ -12,7 +12,7 @@ export default {
})
},
/**
* 生成回归测试说明的被测软件基本信息
* 生成回归测试说明的用例信息
* @returns
*/
createCaseinfo(params = {}) {
@@ -32,5 +32,5 @@ export default {
method: "get",
params
})
}
},
}

View File

@@ -98,5 +98,60 @@ export default {
method: "get",
params
})
},
/**
*
* @returns 生成-静态软件项
*/
createHsmStaticSoft(params = {}) {
return request({
url: `/generateHSM/create/static_soft`,
method: "get",
params
})
},
/**
*
* @returns 生成-静态硬件和固件项
*/
createHsmStaticHard(params = {}) {
return request({
url: `/generateHSM/create/static_hard`,
method: "get",
params
})
},
/**
*
* @returns 生成-动态软件项
*/
createHsmDynamicSoft(params = {}) {
return request({
url: `/generateHSM/create/dynamic_soft`,
method: "get",
params
})
},
/**
*
* @returns 生成-动态硬件项
*/
createHsmDynamicHard(params = {}) {
return request({
url: `/generateHSM/create/dynamic_hard`,
method: "get",
params
})
},
/**
*
* @returns 生成-测试数据
*/
createHsmTestData(params = {}) {
return request({
url: `/generateHSM/create/test_data`,
method: "get",
params
})
}
}

View File

@@ -4,19 +4,26 @@ const AI_API_BASE = import.meta.env.VUE_APP_AI_API_BASE || "http://192.168.0.63:
interface DataRowType {
question: string
stream: boolean
streaming: boolean
model_name: string
user_focus_points: string
project_type: "cpu" | "fpga" | "other"
}
interface DataType {
question: string
project_type: "cpu" | "fpga" | "other"
}
export default {
/**
* 请求AI生成测试项
* @returns 可流式或一次性
* 改为请求测试平台后端然后请求大模型方式
*/
getAiTestItem(data: DataRowType) {
getAiTestItemBackend(data: DataType) {
return request({
url: import.meta.env.DEV ? `/local_doc_qa/testing_item` : `${AI_API_BASE}/api/local_doc_qa/testing_item`,
timeout: 120000,
url: "/local_doc_qa/testing_item",
method: "post",
timeout: 120000,
data
})
}

View File

@@ -0,0 +1,2 @@
// 后端定义的项目时间早于最后一轮次结束时间
export const PROJECT_ENDTIME_ERROR_CODE = 500412

View File

@@ -2,7 +2,7 @@
<div class="static-dynamic-table-container">
<a-modal
v-model:visible="visible"
width="50%"
width="60%"
draggable
:on-before-ok="handleSyncOk"
unmount-on-close
@@ -12,7 +12,7 @@
@close="handleOnClose"
>
<template #title>{{ theTitle }}</template>
<WordLikeTable v-model="tableData" v-model:fontnote="fontnote" />
<WordLikeTable v-model="tableData" v-model:fontnote="fontnote" v-model:rowRounds="tableDataRound" />
</a-modal>
</div>
</template>
@@ -41,9 +41,14 @@ const tableInitValue = [
["", "", ""],
["", "", ""]
]
const tableData = ref(tableInitValue)
const fontnote = ref("")
// 新增一个行属性附带到tableData中
const tableDataRoundInitValue = tableInitValue.map((_) => ["0"])
const tableDataRound = ref(tableDataRoundInitValue)
const handleSyncOk = async () => {
// 验证题注是否为空
if (tableData.value?.length <= 0) {
@@ -56,7 +61,8 @@ const handleSyncOk = async () => {
id: route.query.id,
category: theTitle.value,
table: tableData.value,
fontnote: fontnote.value
fontnote: fontnote.value,
rounds: tableDataRound.value
})
Message.success("保存成功")
} catch (e) {
@@ -81,6 +87,11 @@ const open = async (title: string) => {
const data = res.data
tableData.value = data.table
fontnote.value = data.fontnote || ""
if (data.rounds && data.rounds.length === tableData.value.length) {
tableDataRound.value = data.rounds
} else {
tableDataRound.value = (data.table || tableInitValue).map(() => ["0"])
}
}
visible.value = true
} catch (e) {

View File

@@ -5,7 +5,9 @@
<span>题注</span>
<a-input v-model="fontnote" :style="{ width: '500px' }"></a-input>
</a-space>
<a-alert type="warning" class="mt-2">表格第一行为[表头]自定义表格外例如软/硬件环境测评数据环境差异性分析会自动添加[序号]</a-alert>
<a-alert type="warning" class="mt-2"
>表格第一行为[表头]和轮次无关均会渲染自定义表格外例如软/硬件环境测评数据环境差异性分析会自动添加[序号]</a-alert
>
</div>
<div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
<div class="arco-table-container">
@@ -37,8 +39,8 @@
</a-tooltip>
</span>
</th>
<th class="arco-table-th" :style="{ textAlign: 'center' }">
<span>操作</span>
<th class="arco-table-th w-25" :style="{ textAlign: 'center' }">
<span>适应轮次/操作</span>
</th>
</tr>
</thead>
@@ -51,6 +53,11 @@
</td>
<td class="arco-table-td">
<span class="arco-table-cell items-center justify-center">
<template v-if="datasRounds && rowIndex !== 0">
<a-select size="mini" multiple :style="{ width: '140px' }" placeholder="渲染轮次" v-model="datasRounds![rowIndex]">
<a-option v-for="item in roundOptions" :key="item.value" :value="item.value">{{ item.label }}</a-option>
</a-select>
</template>
<a-tooltip content="此行后新增行">
<a-button type="text" size="mini" @click="addRow(rowIndex)" class="delete-col-btn">
<template #icon>
@@ -75,14 +82,26 @@
</template>
<script setup lang="ts">
// 从仓库读取有多少轮次
import { computed } from "vue"
import { useTreeDataStore } from "@/store"
// 导入中文轮次数组
import tool from "@/utils/tool"
const treeDataStore = useTreeDataStore()
const treeLength = computed(() => treeDataStore.treeData.length)
const roundOptions = computed(() => Array.from({ length: treeLength.value }, (_, i) => ({ value: String(i), label: tool.chnRoundNameArray[i] })))
// 该组件储存数据
const fontnote = defineModel<string>("fontnote")
const datas = defineModel<string[][]>()
// 这里设置undefined一定要判断了
const datasRounds = defineModel<string[][]>("rowRounds", { default: undefined })
// 行列操作
const deleteRow = (rowIndex: number) => {
datas.value!.splice(rowIndex, 1)
if (datasRounds.value) {
datasRounds.value.splice(rowIndex, 1)
}
}
const deleteColumn = (colIndex: number) => {
datas.value!.forEach((row) => {
@@ -92,6 +111,9 @@ const deleteColumn = (colIndex: number) => {
const addRow = (rowIndex: number) => {
const newRow = new Array(datas.value![0].length).fill("")
datas.value!.splice(rowIndex + 1, 0, newRow)
if (datasRounds.value) {
datasRounds.value.splice(rowIndex + 1, 0, ["0"])
}
}
const addColumn = (colIndex: number) => {
// 处理空表格的特殊情况

View File

@@ -5,7 +5,7 @@
</div>
<a-layout class="layout layout-demo">
<a-layout-sider class="layout-sider" :resize-directions="['right']" :width="300">
<div class="p-2 overflow-auto">
<div class="p-2 overflow-auto h-full">
<a-input-group class="mb-2 w-full flex items-center" size="mini">
<a-input style="height: 32px" v-model="searchKey" allow-clear></a-input>
<a-button @click="handleSearchTreeDataClick">搜索</a-button>

View File

@@ -160,18 +160,20 @@ const fetchTestType = async () => {
}
fetchTestType()
const designObj: any = ref()
// 初始化设计需求
const currentKey: string = route.query.key as string
const getDesign = async () => {
try {
const res = await designApi.getDesignDemandOne({ project_id: route.query.id, key: route.query.key })
designObj.value = res.data
const resResult = await designApi.getDesignDemandOne({ project_id: route.query.id, key: route.query.key })
designObj.value = resResult.data
} catch (e) {
console.log("调试错误信息:", e)
Message.error("初始化设计需求信息错误,请检查网络后重试!")
}
}
getDesign()
const designObj: any = ref()
// 进度条和列表加载loading
const percent = ref(0.0)
@@ -188,16 +190,8 @@ const generateClick = async () => {
// 变量给AI的问题
const question = tool.htmlToTextWithDOM(designObj.value?.description || "")
// 请求后处理结果
const res = await aiApi.getAiTestItem({ question: question, stream: false })
// 判断真实接口和开发环境接口
let tempSolve: any = null
if (res.data) {
// 说明是开发环境
tempSolve = res.data
} else {
tempSolve = res
}
const solveRes = JSON.parse(tempSolve.history[0].at(-1))
const res = await aiApi.getAiTestItemBackend({ question: question, project_type: isFPGA ? "fpga" : "cpu" })
const solveRes = JSON.parse(res.data.history[0].at(-1))
console.log("AI生成测试项结果", solveRes)
// 给Vue渲染测试项
dataList.value = solveRes

View File

@@ -136,6 +136,25 @@ const handleModalSubmit = async () => {
Message.error("请添加设计需求后再提交...")
return false
}
// 校验每一条需要填写的字段
for (let i = 0; i < htmlData.value.length; i++) {
const item = htmlData.value[i]
const idx = i + 1 // 第几条,用户友好提示
if (!item.chapter?.trim()) {
Message.error(`${idx} 条需求的章节号不能为空`)
return false
}
if (!item.title?.trim()) {
Message.error(`${idx} 条需求的标题不能为空`)
return false
}
// 标识、内容都可以为空
if (!item.demandType) {
Message.error(`${idx} 条需求的设计需求类型不能为空`)
return false
}
// content 可以为空,不校验
}
const res = await demandApi.multiSave({
projectId: route.query.id,
key: route.query.key,

View File

@@ -11,16 +11,22 @@
<a-timeline-item
v-for="(item, index) in pInfo.time_line.round_time"
:key="index"
:label="`结束时间 : ${item.end}`"
:dot-color="isErrorHighlight && index === pInfo.time_line.round_time.length - 1 ? 'red' : undefined"
>
<template #label>
<span :class="{ 'text-red-500': isErrorHighlight && index === pInfo.time_line.round_time.length - 1 }">结束时间 : {{ item.end }}</span>
</template>
<a-col>
<div>{{ item.name }}</div>
<div class="a-col-title">开始时间 : {{ item.start }}</div>
</a-col>
</a-timeline-item>
<a-timeline-item :label="pInfo.time_line.end_time">
<a-timeline-item :dot-color="isErrorHighlight ? 'red' : undefined">
<template #label>
<span :class="{ 'text-red-500': isErrorHighlight }">{{ pInfo.time_line.end_time }}</span>
</template>
<a-row :style="{ display: 'inline-flex', alignItems: 'center' }">
<div>结束时间</div>
<span>结束时间</span>
</a-row>
</a-timeline-item>
<div class="info">
@@ -32,16 +38,10 @@
<!-- a-modal组件展示生成文档的全部信息 -->
<a-modal v-model:visible="visible" unmount-on-close hide-cancel :closable="false" width="auto">
<template #title> 生成文档时间一览表 </template>
<a-card
:style="{ width: '600px' }"
:title="item.title"
hoverable
v-for="(item, index) in timeList"
:key="item.title"
>
<a-card :style="{ width: '600px' }" :title="item.title" hoverable v-for="(item, index) in timeList" :key="item.title">
<p v-for="(value, key, idx) in item" class="flex">
<template v-if="key !== 'title'">
<span class="font-bold w-[300px]">{{ key }}</span>
<span class="font-bold w-75">{{ key }}</span>
<span class="">{{ value }}</span>
</template>
</p>
@@ -51,8 +51,8 @@
</template>
<script setup>
import { ref } from "vue"
import { useDocTimeShow } from "./useDocTimeShow"
// 在一开始就请求接口
// 1.定义props
const props = defineProps({
pInfo: {
@@ -64,7 +64,19 @@ const props = defineProps({
required: true
}
})
const { visible, handleModalVisible, timeList } = useDocTimeShow(props.projectId)
// 在一开始就请求接口
const { visible, handleModalVisible, timeList } = useDocTimeShow(props.projectId, highlightLastItems)
// 控制错误状态
const isErrorHighlight = ref(false)
function highlightLastItems() {
isErrorHighlight.value = true
}
defineExpose({
highlightLastItems
})
</script>
<style lang="less" scoped>

View File

@@ -1,6 +1,7 @@
import { ref, computed } from "vue"
import projectApi from "@/api/project/project"
export function useDocTimeShow(projectId: number) {
import { PROJECT_ENDTIME_ERROR_CODE } from "@/config/backendErrorCodes"
export function useDocTimeShow(projectId: number, showError: Function) {
const visible = ref<boolean>(false)
const handleModalVisible = (): void => {
visible.value = true
@@ -8,8 +9,15 @@ export function useDocTimeShow(projectId: number) {
// 获取时间模块
const timeTemp = ref<any[]>([])
const getTimeByBackend = async () => {
try {
const res = await projectApi.getDocumentTimeShow(projectId)
timeTemp.value = res.data
} catch (e: any) {
// 如果检测到项目时间错误,则处理一些东西
if (e.data.flag === PROJECT_ENDTIME_ERROR_CODE) {
showError()
}
}
}
getTimeByBackend()
const timeList = computed(() => {
@@ -20,7 +28,7 @@ export function useDocTimeShow(projectId: number) {
item[key] = item[key].join("~")
} else {
if (item[key].includes("年")) {
item[key] = item[key].replace('年','').replace('月','').replace('日','')
item[key] = item[key].replace("年", "").replace("月", "").replace("日", "")
}
}
})

View File

@@ -49,12 +49,12 @@ const useGenerateSecond = function () {
dgGenerateApi.createTopFile({ id }), // 生成顶层技术文件
// 新增拆分接口
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
dgGenerateApi.createStaticSoft({ id, current_round: "0" }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id, current_round: "0" }), // 生成-静态硬件和固件项
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id }), // 生成-测评数据
dgGenerateApi.createDynamicSoft({ id, current_round: "0" }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id, current_round: "0" }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id, current_round: "0" }), // 生成-测评数据
dgGenerateApi.createEnvDiff({ id }), // 生成-环境差异性分析
// 2025年4月21日新增
dgGenerateApi.createIndicates({ id }) // 生成主要功能和性能指标(包括摸底)
@@ -75,12 +75,12 @@ const useGenerateSecond = function () {
smGenerateApi.createSMTechyiju({ id }), // 生成技术类引用文档列表 -> 在大纲基础上添加《测评大纲》
// 拆分软硬件环境
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
dgGenerateApi.createStaticSoft({ id, current_round: "0" }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id, current_round: "0" }), // 生成-静态硬件和固件项
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id }), // 生成-测评数据
dgGenerateApi.createDynamicSoft({ id, current_round: "0" }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id, current_round: "0" }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id, current_round: "0" }), // 生成-测评数据
dgGenerateApi.createEnvDiff({ id }), // 生成-环境差异性分析
// ~~~
smGenerateApi.createSMCaseList({ id }), // 生成用例全
@@ -124,12 +124,12 @@ const useGenerateSecond = function () {
dgGenerateApi.createYiju({ id }), // 生成依据文件
dgGenerateApi.createInterface({ id }), // 生成-被测软件接口 - 和大纲一样
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
hsmGenerateApi.createHsmStaticSoft({ id, current_round: "not0" }), // 生成-静态软件项
hsmGenerateApi.createHsmStaticHard({ id, current_round: "not0" }), // 生成-静态硬件和固件项
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id }), // 生成-测评数据
hsmGenerateApi.createHsmDynamicSoft({ id, current_round: "not0" }), // 生成-动态软件项
hsmGenerateApi.createHsmDynamicHard({ id, current_round: "not0" }), // 生成-动态硬件和固件项
hsmGenerateApi.createHsmTestData({ id, current_round: "not0" }), // 生成-测评数据
dgGenerateApi.createEnvDiff({ id }) // 生成-环境差异性分析
]).finally(() => {
isGenerating.value = false
@@ -187,12 +187,12 @@ const useGenerateSecond = function () {
dgGenerateApi.createInterface({ id }), // 生成-被测软件接口 - 大纲内容
dgGenerateApi.createAbbreviation({ id }), // 生成缩略语 - 大纲内容
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
dgGenerateApi.createStaticSoft({ id, current_round: "last" }), // 生成-静态软件项
dgGenerateApi.createStaticHard({ id, current_round: "last" }), // 生成-静态硬件和固件项
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id }), // 生成-测评数据
dgGenerateApi.createDynamicSoft({ id, current_round: "last" }), // 生成-动态软件项
dgGenerateApi.createDynamicHard({ id, current_round: "last" }), // 生成-动态硬件和固件项
dgGenerateApi.createTestData({ id, current_round: "last" }), // 生成-测评数据
dgGenerateApi.createEnvDiff({ id }), // 生成-环境差异性分析
// 2025年4月29日新增 - 顶层技术文件
dgGenerateApi.createTopFile({ id }) // 生成顶层技术文件

View File

@@ -33,7 +33,19 @@ export default ({ mode }) => {
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: false
},
build: {
chunkSizeWarningLimit: 3000
chunkSizeWarningLimit: 3000,
rolldownOptions: {
output: {
codeSplitting: {
groups: [
{
name: "arco-design",
test: /[\\/]node_modules[\\/]@arco-design[\\/]web-vue[\\/]/
}
]
}
}
}
// assetsPublicPath: "./"
// v8版本又报tinymce is not defined只有遗憾业务js大的问题
/**