文档片段全面改进,不使用render
This commit is contained in:
581
cdTMP/package-lock.json
generated
581
cdTMP/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cdtmp",
|
||||
"private": true,
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,9 +13,9 @@
|
||||
"dependencies": {
|
||||
"@arco-design/color": "^0.4.0",
|
||||
"@arco-design/web-vue": "^2.57.0",
|
||||
"@tanstack/vue-query": "^5.71.1",
|
||||
"@tanstack/vue-query": "^5.74.5",
|
||||
"@tinymce/tinymce-vue": "^6.1.0",
|
||||
"@vueuse/core": "^13.0.0",
|
||||
"@vueuse/core": "^13.1.0",
|
||||
"axios": "^1.8.4",
|
||||
"dayjs": "^1.11.13",
|
||||
"file2md5": "^1.3.0",
|
||||
@@ -23,39 +23,39 @@
|
||||
"mammoth": "^1.9.0",
|
||||
"mitt": "^3.0.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^3.0.1",
|
||||
"pinia": "^3.0.2",
|
||||
"pinyin-match": "^1.2.6",
|
||||
"postcss-import": "^16.1.0",
|
||||
"qs": "^6.14.0",
|
||||
"tinymce": "^7.7.2",
|
||||
"tinymce": "^7.8.0",
|
||||
"vue": "^3.5.13",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-color-kit": "^1.0.6",
|
||||
"vue-data-ui": "^2.6.27",
|
||||
"vue-data-ui": "^2.6.40",
|
||||
"vue-router": "^4.5.0",
|
||||
"vuedraggable": "^2.24.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.0",
|
||||
"@tailwindcss/vite": "^4.1.0",
|
||||
"@tailwindcss/postcss": "^4.1.4",
|
||||
"@tailwindcss/vite": "^4.1.4",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^22.13.17",
|
||||
"@types/node": "^22.14.1",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qs": "^6.9.18",
|
||||
"@vitejs/plugin-vue": "^5.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^4.1.2",
|
||||
"@vue/babel-plugin-jsx": "^1.4.0",
|
||||
"browserslist": "^4.24.4",
|
||||
"eslint": "^9.23.0",
|
||||
"eslint": "^9.25.0",
|
||||
"eslint-plugin-vue": "^10.0.0",
|
||||
"less": "^4.2.2",
|
||||
"less": "^4.3.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"postcss": "^8.5.3",
|
||||
"prettier": "^3.5.3",
|
||||
"rollup-plugin-visualizer": "^5.14.0",
|
||||
"tailwindcss": "^4.1.0",
|
||||
"typescript": "^5.8.2",
|
||||
"vite": "^6.2.4",
|
||||
"vue-eslint-parser": "^10.1.1"
|
||||
"tailwindcss": "^4.1.4",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^6.3.2",
|
||||
"vue-eslint-parser": "^10.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,15 +308,4 @@ export default {
|
||||
params
|
||||
})
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @returns 生成-主要战技指标
|
||||
*/
|
||||
createMainTech(params = {}) {
|
||||
return request({
|
||||
url: `/generate/create/mainTech`,
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
15
cdTMP/src/api/generate/fragment.ts
Normal file
15
cdTMP/src/api/generate/fragment.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { request } from "@/api/request"
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 根据项目id和文档类型获取文档片段名称
|
||||
* @returns 文档片段名称数组
|
||||
*/
|
||||
getFragmentByDocumentType(params: { id: number; documentType: string }) {
|
||||
return request({
|
||||
url: `/createfragment/get_fragments`,
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
13
cdTMP/src/api/generate/other.ts
Normal file
13
cdTMP/src/api/generate/other.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { request } from "@/api/request"
|
||||
export default {
|
||||
/**
|
||||
* 获取当前项目非第一轮有几轮测试
|
||||
*/
|
||||
getHgRoundNumber(params: { id: number }) {
|
||||
return request({
|
||||
url: `/createfragment/get_round_exit`,
|
||||
method: "get",
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,11 @@ export default {
|
||||
* 如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir生成最终大纲文档
|
||||
*/
|
||||
createDagangSeiTai(params = {}) {
|
||||
createDagangSeiTai(data = {}) {
|
||||
return request({
|
||||
url: `/create/dgDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
@@ -16,11 +16,11 @@ export default {
|
||||
* 如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir以及output_dir/sm中文档生成测试说明
|
||||
*/
|
||||
createShuomingSeiTai(params = {}) {
|
||||
createShuomingSeiTai(data = {}) {
|
||||
return request({
|
||||
url: `/create/smDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
@@ -28,23 +28,11 @@ export default {
|
||||
* TODO:如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir以及output_dir/JL中文档生成测试记录
|
||||
*/
|
||||
createJiluSeiTai(params = {}) {
|
||||
createJiluSeiTai(data = {}) {
|
||||
return request({
|
||||
url: `/create/jlDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
/**
|
||||
* TODO:如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir以及output_dir/bg中文档生成测评报告
|
||||
*/
|
||||
createBgDocument(params = {}) {
|
||||
return request({
|
||||
url: `/create/bgDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
@@ -52,11 +40,11 @@ export default {
|
||||
* TODO:如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir以及output_dir/hsm中文档生成回归测试说明(特殊多个文件)
|
||||
*/
|
||||
createHsmDocument(params = {}) {
|
||||
createHsmDocument(data = {}) {
|
||||
return request({
|
||||
url: `/create/hsmDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
@@ -64,11 +52,11 @@ export default {
|
||||
* TODO:如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir以及output_dir/hjl中文档生成回归测试记录(特殊多个文件)
|
||||
*/
|
||||
createHjlDocument(params = {}) {
|
||||
createHjlDocument(data = {}) {
|
||||
return request({
|
||||
url: `/create/hjlDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
@@ -76,11 +64,23 @@ export default {
|
||||
* TODO:生成最终问题单
|
||||
* @returns 返回是否正确生成问题单
|
||||
*/
|
||||
createWtdDocument(params = {}) {
|
||||
createWtdDocument(data = {}) {
|
||||
return request({
|
||||
url: `/create/wtdDocument`,
|
||||
method: "get",
|
||||
params,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
/**
|
||||
* TODO:如果缺少部分文件给与提示
|
||||
* @returns 根据output_dir以及output_dir/bg中文档生成测评报告
|
||||
*/
|
||||
createBgDocument(data = {}) {
|
||||
return request({
|
||||
url: `/create/bgDocument`,
|
||||
method: "post",
|
||||
data,
|
||||
responseType: "blob"
|
||||
})
|
||||
},
|
||||
@@ -92,7 +92,7 @@ export default {
|
||||
return request({
|
||||
url: `/create/cancel`,
|
||||
method: "get",
|
||||
params,
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,28 @@ function createService() {
|
||||
return res.data
|
||||
},
|
||||
(error) => {
|
||||
const err = (text) => {
|
||||
Message.error({
|
||||
content:
|
||||
const err = async (text) => {
|
||||
let content = ""
|
||||
// 在设置axios为responseType: "blob"时候,data为Blob对象,需要解析Blob
|
||||
if (error.response && error.response.data instanceof Blob) {
|
||||
try {
|
||||
const text = await error.response.data.text()
|
||||
content = JSON.parse(text).message && JSON.parse(text).message
|
||||
if (!content) {
|
||||
content = "未知Blob错误"
|
||||
}
|
||||
} catch (e) {
|
||||
Message.error("解析Blob失败")
|
||||
}
|
||||
} else {
|
||||
// 非Blob正常错误响应
|
||||
content =
|
||||
error.response && error.response.data && error.response.data.message
|
||||
? error.response.data.message
|
||||
: text,
|
||||
: text
|
||||
}
|
||||
Message.error({
|
||||
content,
|
||||
icon: () => h(IconFaceFrownFill)
|
||||
})
|
||||
}
|
||||
@@ -58,6 +74,9 @@ function createService() {
|
||||
case 500:
|
||||
err("服务器内部错误")
|
||||
break
|
||||
case 400:
|
||||
err("服务器抛出逻辑错误")
|
||||
break
|
||||
case 401:
|
||||
err("登录状态已过期,需要重新登录")
|
||||
// 清除本地localStorage
|
||||
|
||||
7
cdTMP/src/arco-design-types.d.ts
vendored
Normal file
7
cdTMP/src/arco-design-types.d.ts
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { ArcoGlobalComponents } from "@arco-design/web-vue"
|
||||
|
||||
declare module "vue" {
|
||||
export interface GlobalComponents extends ArcoGlobalComponents {
|
||||
ASpace: (typeof import("@arco-design/web-vue"))["Space"]
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue"
|
||||
const text = ref("暂无数据")
|
||||
<script setup lang="ts">
|
||||
// 标准vue3.5+的默认值写法、结构写法、ts类型写法
|
||||
const { text = "暂无数据" } = defineProps<{ text?: string }>()
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
@@ -379,9 +379,4 @@ const refresh = () => {
|
||||
defineExpose({ deleteAction, recoveryAction })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.arco-image-img) {
|
||||
object-fit: contain;
|
||||
background-color: var(--color-fill-4);
|
||||
}
|
||||
</style>
|
||||
<style scoped></style>
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<a-drawer :visible="visible" unmountOnClose :footer="false" :width="950" @cancel="onCancel">
|
||||
<template #title>设置</template>
|
||||
|
||||
@@ -136,9 +136,9 @@
|
||||
<a-tooltip content="显隐搜索"
|
||||
><a-button shape="circle" @click="toggleSearch"><icon-search /></a-button
|
||||
></a-tooltip>
|
||||
<a-tooltip content="打印表格"
|
||||
<!-- <a-tooltip content="打印表格"
|
||||
><a-button shape="circle" @click="printTable"><icon-printer /></a-button
|
||||
></a-tooltip>
|
||||
></a-tooltip> -->
|
||||
<a-tooltip content="设置"
|
||||
><a-button shape="circle" @click="tableSetting"><icon-settings /></a-button
|
||||
></a-tooltip>
|
||||
@@ -809,9 +809,9 @@ const tabChange = async (value) => {
|
||||
await refresh()
|
||||
}
|
||||
|
||||
const printTable = () => {
|
||||
new Print(crudContentRef.value)
|
||||
}
|
||||
// const printTable = () => {
|
||||
// new Print(crudContentRef.value)
|
||||
// }
|
||||
|
||||
const openContextMenu = (ev, record) => {
|
||||
options.value?.contextMenu?.enabled === true && crudContextMenuRef.value.openContextMenu(ev, record)
|
||||
@@ -821,9 +821,9 @@ const execContextMenuCommand = async (args) => {
|
||||
const item = args.contextItem
|
||||
const record = args.record
|
||||
switch (item.operation) {
|
||||
case "print":
|
||||
await printTable()
|
||||
break
|
||||
// case "print":
|
||||
// await printTable()
|
||||
// break
|
||||
case "refresh":
|
||||
await refresh()
|
||||
break
|
||||
|
||||
@@ -57,8 +57,11 @@ const props = defineProps({
|
||||
type: [String, Array],
|
||||
// 如果要取消粘贴只粘贴文本,需要用户加格式请加上pastetext
|
||||
default:
|
||||
"code undo redo restoredraft | paste | bold | aligncenter alignleft alignjustify indent | \
|
||||
styleselect formatselect fontselect fontsizeselect | bullist numlist | removeformat"
|
||||
"undo redo aligncenter alignleft indent styleselect formatselect fontselect fontsizeselect removeformat"
|
||||
|
||||
// 下面是备份配置:
|
||||
// default:"code undo redo restoredraft | paste | bold | aligncenter alignleft alignjustify indent | \
|
||||
// styleselect formatselect fontselect fontsizeselect | bullist numlist | removeformat"
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
162
cdTMP/src/components/ma-form/Customs/ChenDemandList.vue
Normal file
162
cdTMP/src/components/ma-form/Customs/ChenDemandList.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div class="chen-demand-list">
|
||||
<div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
|
||||
<div class="arco-table-container">
|
||||
<table class="arco-table-element" cellpadding="0" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="arco-table-tr">
|
||||
<th class="arco-table-th" :width="20">
|
||||
<span class="arco-table-cell arco-table-cell-align-center">
|
||||
<a-tooltip content="添加步骤">
|
||||
<a-button type="primary" size="mini" shape="round" @click="addItem">
|
||||
新增步骤+
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</th>
|
||||
<th class="arco-table-th" :width="400">
|
||||
<span class="arco-table-cell arco-table-cell-align-center">
|
||||
<span class="arco-table-th-title">操作</span>
|
||||
</span>
|
||||
</th>
|
||||
<th class="arco-table-th" :width="310">
|
||||
<span class="arco-table-cell arco-table-cell-align-center">
|
||||
<span class="arco-table-th-title">预期</span>
|
||||
</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- 根据subStep渲染 -->
|
||||
<template
|
||||
v-if="modelValue && modelValue.length > 0"
|
||||
v-for="(stepItem, index) in modelValue"
|
||||
:key="index"
|
||||
>
|
||||
<tr class="arco-table-tr">
|
||||
<td class="arco-table-td">
|
||||
<span class="arco-table-cell justify-center gap-1.5">
|
||||
<!-- 删除单项按钮 -->
|
||||
<a-button
|
||||
type="primary"
|
||||
status="danger"
|
||||
size="mini"
|
||||
shape="round"
|
||||
@click="deleteItem(index)"
|
||||
:disabled="modelValue.length <= 1"
|
||||
>
|
||||
<template #icon><icon-close /></template>
|
||||
</a-button>
|
||||
<a-tooltip content="复制该项添加">
|
||||
<a-button
|
||||
type="primary"
|
||||
status="warning"
|
||||
size="mini"
|
||||
shape="round"
|
||||
@click="copyItem(index)"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-copy />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-button-group shape="round" size="mini">
|
||||
<a-button type="primary" @click="moveUp(index)">
|
||||
<icon-arrow-rise />
|
||||
</a-button>
|
||||
<a-button type="primary" @click="moveDown(index)">
|
||||
<icon-arrow-fall />
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
</span>
|
||||
</td>
|
||||
<td class="arco-table-td">
|
||||
<span class="arco-table-cell">
|
||||
<a-textarea auto-size v-model="stepItem.operation"></a-textarea>
|
||||
</span>
|
||||
</td>
|
||||
<td class="arco-table-td">
|
||||
<span class="arco-table-cell">
|
||||
<a-textarea auto-size v-model="stepItem.expect"></a-textarea>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template v-else>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<div class="flex justify-center items-center p-2 border-1">
|
||||
<a-alert>暂无测试子项条目,请添加</a-alert>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Ref, nextTick } from "vue"
|
||||
|
||||
// 辅助函数:当modelValue不是列表时候
|
||||
function handleIsNotArray() {
|
||||
if (Array.isArray(modelValue.value)) return
|
||||
modelValue.value = []
|
||||
}
|
||||
|
||||
// 定义value数据
|
||||
interface ISubStep {
|
||||
operation: string
|
||||
expect: string
|
||||
}
|
||||
// 绑定外部数据,预期是列表,有operation、expect两个字段
|
||||
const modelValue = defineModel() as unknown as Ref<ISubStep[]>
|
||||
|
||||
// 单纯新增 - modalValue新增一行
|
||||
const addItem = async () => {
|
||||
handleIsNotArray()
|
||||
await nextTick() // 因为要等待modelValue值变化DOM渲染完毕
|
||||
modelValue.value.push({ operation: "", expect: "" })
|
||||
}
|
||||
|
||||
// 删除指定行
|
||||
const deleteItem = (index: number) => {
|
||||
modelValue.value = modelValue.value.filter((_, idx) => idx !== index)
|
||||
}
|
||||
|
||||
// 复制单项
|
||||
const copyItem = (index: number) => {
|
||||
const newItem = modelValue.value[index]
|
||||
modelValue.value = [...modelValue.value, newItem]
|
||||
}
|
||||
|
||||
// 辅助函数交换列表两个元素
|
||||
function swapItems(idx1: number, idx2: number) {
|
||||
const arr = modelValue.value
|
||||
;[arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]]
|
||||
}
|
||||
|
||||
// 向下移动
|
||||
const moveDown = (index: number) => {
|
||||
if (index === modelValue.value.length - 1) return
|
||||
swapItems(index, index + 1)
|
||||
}
|
||||
|
||||
// 向上移动
|
||||
const moveUp = (index: number) => {
|
||||
if (index === 0) return
|
||||
swapItems(index, index - 1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
:deep(.arco-form-item-content-flex) {
|
||||
display: block;
|
||||
}
|
||||
:deep(.arco-table-cell .arco-form-item) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<a-form-item
|
||||
v-if="typeof props.component.display == 'undefined' || props.component.display === true"
|
||||
@@ -236,7 +232,7 @@ function swapItems(idx1, idx2) {
|
||||
;[arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]]
|
||||
}
|
||||
|
||||
// 修改源码上移动和下移动
|
||||
// 修改源码:上移动和下移动
|
||||
const moveUp = (itemIndex) => {
|
||||
const itemLength = formModel.value[props.component.dataIndex].length
|
||||
// 如果是第一个,不做操作
|
||||
@@ -290,9 +286,15 @@ onMounted(async () => {
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
<style scoped lang="less">
|
||||
:deep(.arco-form-item-content-flex) {
|
||||
display: block;
|
||||
position: relative;
|
||||
.arco-radio-group {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-40%);
|
||||
}
|
||||
}
|
||||
:deep(.arco-table-cell .arco-form-item) {
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<td
|
||||
v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<table
|
||||
v-if="typeof props.component?.display == 'undefined' || props.component?.display === true"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<ma-form-item
|
||||
v-if="typeof props.component.display == 'undefined' || props.component.display === true"
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<a-form-item
|
||||
:label="props.component.title"
|
||||
|
||||
@@ -341,7 +341,7 @@ const handleStdFormSubmit = async (data, done) => {
|
||||
}
|
||||
dictList.value["standard"].push(newInfo)
|
||||
// 清空当前数据
|
||||
stdFormData.value = initStdFormData
|
||||
stdFormData.value = { ...initStdFormData }
|
||||
Notification.success("添加成功,请回到输入框进行选择")
|
||||
} catch (err) {
|
||||
Notification.error("请求错误,请重试或在数据管理页面添加")
|
||||
|
||||
53
cdTMP/src/components/ma-form/formItem/form-steptable.vue
Normal file
53
cdTMP/src/components/ma-form/formItem/form-steptable.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<!-- 组件外部的 form-item -->
|
||||
<ma-form-item
|
||||
v-if="typeof props.component.display == 'undefined' || props.component.display === true"
|
||||
:component="props.component"
|
||||
:custom-field="props.customField"
|
||||
>
|
||||
<slot :name="`form-${props.component.dataIndex}`" v-bind="props.component">
|
||||
<!-- 调用自己开发的自定义组件,具体名称改成自己的 -->
|
||||
<ChenDemandList v-model="value"></ChenDemandList>
|
||||
</slot>
|
||||
</ma-form-item>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// ~~~~start~~~~
|
||||
import ChenDemandList from "../Customs/ChenDemandList.vue"
|
||||
// ~~~~end~~~~
|
||||
import { ref, inject, onMounted, watch } from "vue"
|
||||
// 引入处理索引的函数
|
||||
import { get, set } from "lodash-es"
|
||||
// 引入 MaFormItem 组件
|
||||
import MaFormItem from "./form-item.vue"
|
||||
// 引入处理事件的函数
|
||||
import { maEvent } from "../js/formItemMixin.js"
|
||||
// 组件都需要定义以下的props
|
||||
const props = defineProps({
|
||||
component: Object,
|
||||
customField: { type: String, default: undefined }
|
||||
})
|
||||
// form数据列表
|
||||
const formModel = inject("formModel")
|
||||
// 该组件在form数据的索引名称
|
||||
const index = props.customField ?? props.component.dataIndex
|
||||
// 该组件的表单数据
|
||||
const value = ref(get(formModel.value, index))
|
||||
|
||||
// 监听组件数据的改变
|
||||
watch(
|
||||
() => get(formModel.value, index),
|
||||
(vl) => (value.value = vl)
|
||||
)
|
||||
watch(
|
||||
() => value.value,
|
||||
(v) => set(formModel.value, index, v)
|
||||
)
|
||||
|
||||
// 绑定组件事件
|
||||
maEvent.handleCommonEvent(props.component, "onCreated")
|
||||
onMounted(() => {
|
||||
maEvent.handleCommonEvent(props.component, "onMounted")
|
||||
})
|
||||
</script>
|
||||
@@ -1,7 +1,3 @@
|
||||
<!--
|
||||
- @Author XXX
|
||||
- @Link XXX
|
||||
-->
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<a-spin :loading="formLoading" :tip="options.loadingText" class="w-full ma-form-spin">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
测试管理平台
|
||||
</div>
|
||||
</a-typography-title>
|
||||
<a-typography-title :heading="6" class="version">V0.0.4</a-typography-title>
|
||||
<a-typography-title :heading="6" class="version">v{{ $version }}</a-typography-title>
|
||||
<icon-menu-fold
|
||||
v-if="!topMenu && appStore.device === 'mobile'"
|
||||
style="font-size: 22px; cursor: pointer"
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
<!-- 关联的modal组件 -->
|
||||
<a-modal v-model:visible="modalVisible" :width="700" draggable :on-before-ok="handleCopyDemand">
|
||||
<template #title>复制到设计需求</template>
|
||||
<div class="pb-3">选择复制到的节点:</div>
|
||||
<div class="pb-3">选择复制到的节点(<span class="point">支持搜索</span>):</div>
|
||||
<a-cascader
|
||||
:options="options"
|
||||
allow-search
|
||||
@@ -432,4 +432,7 @@ const {
|
||||
.chen-node-title {
|
||||
cursor: help;
|
||||
}
|
||||
.point {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -48,11 +48,6 @@ export default function useRoundMaForm(projectId: Ref<string>, handleSoDutExists
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "速度等级",
|
||||
dataIndex: "speedGrade",
|
||||
placeholder: "请填入速度等级"
|
||||
},
|
||||
{
|
||||
title: "动态地点",
|
||||
dataIndex: "location",
|
||||
@@ -82,11 +77,6 @@ export default function useRoundMaForm(projectId: Ref<string>, handleSoDutExists
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "封装",
|
||||
dataIndex: "package",
|
||||
placeholder: "请填入封装"
|
||||
},
|
||||
{
|
||||
title: "质量等级",
|
||||
dataIndex: "grade",
|
||||
@@ -100,6 +90,7 @@ export default function useRoundMaForm(projectId: Ref<string>, handleSoDutExists
|
||||
]
|
||||
},
|
||||
placeholder: "请填入质量等级",
|
||||
addDefaultValue: "1",
|
||||
rules: [{ required: true, message: "质量等级必填" }]
|
||||
}
|
||||
]
|
||||
@@ -110,7 +101,7 @@ export default function useRoundMaForm(projectId: Ref<string>, handleSoDutExists
|
||||
},
|
||||
{
|
||||
formType: "card",
|
||||
title: "极端工况信息",
|
||||
title: "极端工况信息(FPGA)",
|
||||
customClass: ["mb-2", "pb-0"],
|
||||
bodyStyle: { paddingBottom: 0 },
|
||||
formList: [
|
||||
|
||||
@@ -43,6 +43,7 @@ for (const path in modules) {
|
||||
app.config.globalProperties.$tool = tool
|
||||
app.config.globalProperties.$title = import.meta.env.VITE_APP_TITLE
|
||||
app.config.globalProperties.$url = import.meta.env.VITE_APP_BASE
|
||||
app.config.globalProperties.$version = packageJson.version
|
||||
app.mount("#app")
|
||||
|
||||
// show version tag in console
|
||||
|
||||
@@ -66,8 +66,9 @@ const useUserStore = defineStore("user", {
|
||||
|
||||
// login函数,传一个form信息
|
||||
login(form) {
|
||||
const form_data = { username: form.username, password: form.password }
|
||||
return loginAPI
|
||||
.login(form)
|
||||
.login(form_data)
|
||||
.then((res) => {
|
||||
if (res.success === true) {
|
||||
this.setToken(res.data.token)
|
||||
|
||||
@@ -59,6 +59,9 @@ function createService() {
|
||||
case 500:
|
||||
err("服务器内部错误")
|
||||
break
|
||||
case 400:
|
||||
err("服务器抛出逻辑错误")
|
||||
break
|
||||
case 401:
|
||||
err("登录状态已过期,需要重新登录")
|
||||
// 清楚本地localStorage
|
||||
|
||||
@@ -5,3 +5,15 @@ export interface IDictData<T> {
|
||||
label: T
|
||||
value: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 产品文件的类型
|
||||
*/
|
||||
export type DocumentType =
|
||||
| "测评大纲"
|
||||
| "测试说明"
|
||||
| "测试记录"
|
||||
| "回归测试说明"
|
||||
| "回归测试记录"
|
||||
| "问题单"
|
||||
| "测评报告"
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Ref, computed } from "vue"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useAppStore } from "@/store"
|
||||
|
||||
// 单个每月项目数量对象格式
|
||||
interface IData {
|
||||
@@ -11,6 +13,9 @@ interface ResData {
|
||||
}
|
||||
|
||||
function useVueDataUI(data: Ref<ResData>) {
|
||||
const appStore = useAppStore()
|
||||
const { theme } = storeToRefs(appStore)
|
||||
// 结构pinia储存的主体响应式变量
|
||||
const initialData = [
|
||||
{
|
||||
name: "项目数量",
|
||||
@@ -29,8 +34,10 @@ function useVueDataUI(data: Ref<ResData>) {
|
||||
}
|
||||
return initialData
|
||||
})
|
||||
// 暗黑模式识别(这是存在pinia的)
|
||||
const darkMode = document.body.getAttribute("arco-theme")
|
||||
const initialConfig = {
|
||||
theme: "",
|
||||
theme: darkMode === "dark" ? "celebrationNight" : "",
|
||||
responsive: false,
|
||||
customPalette: [],
|
||||
downsample: { threshold: 500 },
|
||||
@@ -108,7 +115,7 @@ function useVueDataUI(data: Ref<ResData>) {
|
||||
fontSize: 18,
|
||||
bold: true,
|
||||
textAlign: "left",
|
||||
paddingLeft: 0,
|
||||
paddingLeft: 5,
|
||||
paddingRight: 0,
|
||||
subtitle: { color: "#CCCCCCff", text: "", fontSize: 16, bold: false },
|
||||
show: true
|
||||
@@ -168,7 +175,10 @@ function useVueDataUI(data: Ref<ResData>) {
|
||||
const countData = data.value.data.map((it) => it.count)
|
||||
initialConfig.chart.grid.labels.yAxis.scaleMax = Math.max(...countData) ? Math.max(...countData) : 10
|
||||
}
|
||||
return initialConfig
|
||||
return {
|
||||
...initialConfig,
|
||||
theme: theme.value === "dark" ? "celebrationNight" : ""
|
||||
}
|
||||
})
|
||||
return { chartData, chartConfig }
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="text-center leading-[32px]">管理平台版本:</div>
|
||||
</div>
|
||||
<div class="mt-2 leading-[32px]">
|
||||
<a-tag class="w-fit h-[32px]" color="#0fc6c2">TestPlant V0.0.4</a-tag>
|
||||
<a-tag class="w-fit h-[32px]" color="#0fc6c2">TestPlant v{{ $version }}</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -105,10 +105,10 @@ onMounted(async () => {
|
||||
width: 75px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 65px;
|
||||
font-weight: bold;
|
||||
font-size: 1.3em;
|
||||
|
||||
border-radius: 2px 0 0 2px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<img class="w-[200px] h-[300px]" src="@/assets/img/ErrorLoad.svg" alt="" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<VueUiXy :dataset="chartData" :config="chartConfig" />
|
||||
<VueUiXy :dataset="chartData" :config="chartConfig" :style="{ padding: '10px' }" />
|
||||
</template>
|
||||
</div>
|
||||
</a-spin>
|
||||
@@ -18,12 +18,14 @@ import commonApi from "@/api/common"
|
||||
import { VueUiXy } from "vue-data-ui"
|
||||
import useVueDataUI from "@/views/dashboard/workplace/components/cpns/hooks/vueDataUI"
|
||||
import { useQuery } from "@tanstack/vue-query"
|
||||
|
||||
// vue-query请求图表接口
|
||||
const { isPending, data, isError } = useQuery({
|
||||
queryKey: ["chart"],
|
||||
queryFn: commonApi.getChartData,
|
||||
refetchOnWindowFocus: false
|
||||
})
|
||||
|
||||
// vue-data-ui图表
|
||||
const { chartData, chartConfig } = useVueDataUI(data)
|
||||
</script>
|
||||
|
||||
@@ -108,7 +108,6 @@ import { useUserStore } from "@/store"
|
||||
import { useRouter, useRoute } from "vue-router"
|
||||
import userApi from "@/api/system/user"
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
// 绑定登录form的数据
|
||||
// const form = reactive({ username: "superAdmin", password: "admin123", code: "" })
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { defineComponent } from "vue"
|
||||
import { defineComponent, ref } from "vue"
|
||||
import { TreeNodeData } from "@arco-design/web-vue"
|
||||
import { useTreeDataStore } from "@/store"
|
||||
import testDemandAPI from "@/api/project/testDemand"
|
||||
import useOptions from "./useOptions"
|
||||
import subFormHooks from "@/views/project/projPublicHooks/subFormHooks"
|
||||
import useBeforeCancel from "@/views/project/projPublicHooks/useBeforeCancel"
|
||||
import { cloneDeep } from "lodash-es"
|
||||
|
||||
const DemandSubForm = defineComponent({
|
||||
name: "DemandSubFormForm",
|
||||
@@ -25,6 +27,8 @@ const DemandSubForm = defineComponent({
|
||||
// 设置表单名称
|
||||
title.value = nodeData.title!
|
||||
const res = await testDemandAPI.getTestDemandOne({ project_id, key }) // **API变化**
|
||||
// 得到数据时候将beforeFormContent搞定
|
||||
beforeFormContent.value = cloneDeep(res.data.testContent)
|
||||
// 更新表单
|
||||
formData.value = res.data // **属性变化**
|
||||
formData.value.round = key.split("-")[0]
|
||||
@@ -38,10 +42,13 @@ const DemandSubForm = defineComponent({
|
||||
// out use
|
||||
expose({ open })
|
||||
|
||||
// hook-判断是否更变内容关闭-只用于测试项和测试用例
|
||||
const beforeFormContent = ref<any>(undefined)
|
||||
const { handleBeforeCancel } = useBeforeCancel(formData, beforeFormContent, visible)
|
||||
// Dom
|
||||
return () => (
|
||||
// 注意v-model:visible是不能放在对象解构的
|
||||
<a-modal {...modalOptions} v-model:visible={visible.value}>
|
||||
<a-modal {...modalOptions} v-model:visible={visible.value} on-before-cancel={handleBeforeCancel}>
|
||||
{{
|
||||
title: () => <span>[设计需求]-{title.value}</span>,
|
||||
default: () => (
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function (crudOrFormRef: any) {
|
||||
dict: { name: "testType", translation: true, props: { label: "title", value: "key" } },
|
||||
extra: "支持拼音搜索,例如:gn可以搜索出功能测试",
|
||||
// 这是arco的属性,所以在ma-crud和ma-form可以直接使用arco属性和事件(事件+onXXX)
|
||||
filterOption: function (inputValue, selectedOption) {
|
||||
filterOption: function (inputValue: any, selectedOption: any) {
|
||||
if (inputValue) {
|
||||
let matchRes = PinYinMatch.match(selectedOption.label, inputValue)
|
||||
if (matchRes) {
|
||||
@@ -91,7 +91,7 @@ export default function (crudOrFormRef: any) {
|
||||
dataIndex: "testDesciption",
|
||||
formType: "textarea",
|
||||
maxLength: 256,
|
||||
placeholder: "FPGA-老版本需填写!!!"
|
||||
placeholder: "请填写整体测试项的描述"
|
||||
},
|
||||
{
|
||||
title: "测试子项",
|
||||
@@ -99,53 +99,28 @@ export default function (crudOrFormRef: any) {
|
||||
dataIndex: "testContent",
|
||||
commonRules: [{ required: true, message: "测试方法是必填的" }],
|
||||
formType: "children-form",
|
||||
type: "group",
|
||||
formList: [
|
||||
{
|
||||
title: "子项名称",
|
||||
dataIndex: "subName",
|
||||
placeholder: "对应测试项描述标题,和测试方法的标题",
|
||||
rules: [{ required: true, message: "测试子项名称必填" }],
|
||||
onChange: (ev) => {
|
||||
onChange: (ev: any) => {
|
||||
// 取出子项的对象数组
|
||||
const subItemFormData = crudOrFormRef.value.getFormData().testContent
|
||||
// 取出充分性条件字段字符串
|
||||
const mapRes = subItemFormData.map((subItem) => subItem.subName)
|
||||
const mapRes = subItemFormData.map((subItem: any) => subItem.subName)
|
||||
crudOrFormRef.value.getFormData().adequacy = `测试用例覆盖${mapRes.join(
|
||||
"、"
|
||||
)}子项要求的全部内容。\n所有用例执行完毕,对于未执行的用例说明未执行原因。`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "子项描述",
|
||||
dataIndex: "subDesc",
|
||||
formType: "textarea",
|
||||
placeholder: "对应大纲测试项表格的测试项描述,FPGA-老模版不用填写!!!"
|
||||
// rules: [{ required: true, message: "测试子项描述必填" }]
|
||||
title: "操作与预期",
|
||||
dataIndex: "subStep",
|
||||
formType: "steptable"
|
||||
},
|
||||
{
|
||||
title: "条件",
|
||||
dataIndex: "condition",
|
||||
formType: "textarea",
|
||||
placeholder: "在什么环境和前置条件下"
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
dataIndex: "operation",
|
||||
formType: "textarea",
|
||||
placeholder: "通过xxx操作"
|
||||
},
|
||||
{
|
||||
title: "观察",
|
||||
dataIndex: "observe",
|
||||
formType: "textarea",
|
||||
placeholder: "查看xxx内容"
|
||||
},
|
||||
{
|
||||
title: "期望",
|
||||
dataIndex: "expect",
|
||||
formType: "textarea",
|
||||
placeholder: "xxx结果正确"
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
|
||||
32
cdTMP/src/views/project/projPublicHooks/useBeforeCancel.ts
Normal file
32
cdTMP/src/views/project/projPublicHooks/useBeforeCancel.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { isEqual } from "lodash-es"
|
||||
import { getCurrentInstance } from "vue"
|
||||
|
||||
/**
|
||||
* 该hook为测试项子项和测试用例步骤设计,当其改变时候弹窗通知用户
|
||||
*/
|
||||
export default function useBeforeCancel(formData: any, beforeFormContent: any, visible: any) {
|
||||
const app = getCurrentInstance()!.appContext.config.globalProperties
|
||||
const handleBeforeCancel = () => {
|
||||
if (!beforeFormContent.value) {
|
||||
return true
|
||||
}
|
||||
const content = formData.value.testContent || formData.value.testStep
|
||||
const iuEqualValue = isEqual(content, beforeFormContent.value)
|
||||
!iuEqualValue &&
|
||||
app.$modal.confirm({
|
||||
title: "测试项步骤内容你已改动,是否保留您编写的测试项/测试用例步骤数据?",
|
||||
content: "",
|
||||
okText: "返回重新编辑",
|
||||
cancelText: "取消",
|
||||
simple: true,
|
||||
onOk: () => {
|
||||
visible.value = true
|
||||
},
|
||||
onCancel: () => {}
|
||||
})
|
||||
return true
|
||||
}
|
||||
return {
|
||||
handleBeforeCancel
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { defineComponent } from "vue"
|
||||
import { defineComponent, ref } from "vue"
|
||||
import { Message, TreeNodeData } from "@arco-design/web-vue"
|
||||
import { useTreeDataStore } from "@/store"
|
||||
import caseApi from "@/api/project/case"
|
||||
import useOptions from "./useOptions"
|
||||
import subFormHooks from "@/views/project/projPublicHooks/subFormHooks"
|
||||
import useBeforeCancel from "@/views/project/projPublicHooks/useBeforeCancel"
|
||||
import { cloneDeep } from "lodash-es"
|
||||
|
||||
const CaseSubForm = defineComponent({
|
||||
name: "DemandSubFormForm",
|
||||
@@ -26,6 +28,8 @@ const CaseSubForm = defineComponent({
|
||||
title.value = nodeData.title!
|
||||
// 注意这里因为case接口原因,这里需要projectId!!!!!!!!!!!!!!!
|
||||
const res = await caseApi.getCaseOne({ projectId: project_id, key }) // **API变化**
|
||||
// 得到数据时候将beforeFormContent搞定
|
||||
beforeFormContent.value = cloneDeep(res.data.testStep)
|
||||
// 更新表单
|
||||
formData.value = res.data // **属性变化**
|
||||
formData.value.round = key.split("-")[0]
|
||||
@@ -41,10 +45,14 @@ const CaseSubForm = defineComponent({
|
||||
// out use
|
||||
expose({ open })
|
||||
|
||||
// hook-判断是否更变内容关闭-只用于测试项和测试用例
|
||||
const beforeFormContent = ref<any>(undefined)
|
||||
const { handleBeforeCancel } = useBeforeCancel(formData, beforeFormContent, visible)
|
||||
|
||||
// Dom
|
||||
return () => (
|
||||
// 注意v-model:visible是不能放在对象解构的
|
||||
<a-modal {...modalOptions} v-model:visible={visible.value}>
|
||||
<a-modal {...modalOptions} v-model:visible={visible.value} on-before-cancel={handleBeforeCancel}>
|
||||
{{
|
||||
title: () => <span>[设计需求]-{title.value}</span>,
|
||||
default: () => (
|
||||
|
||||
@@ -157,7 +157,7 @@ export default function (crudOrFormRef: any, problemFormRef?: any) {
|
||||
}
|
||||
],
|
||||
formType: "children-form",
|
||||
type: "group",
|
||||
type: "group", // 注意这里可能改样式"group"/"table"
|
||||
formList: [
|
||||
{
|
||||
title: "操作",
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div class="hg-doc-upload-container">
|
||||
<div v-if="roundNum === 0">
|
||||
<a-alert type="warning">暂无回归测试轮次信息</a-alert>
|
||||
</div>
|
||||
<a-tabs v-else :default-active-key="1" type="line">
|
||||
<a-tab-pane v-for="n in roundNum" :key="n" :title="`第${n + 1}轮${documentType}`">
|
||||
<a-upload
|
||||
:action="`/api/documentUpload/file?id=${projectId}&documentType=${documentType}&round_num=${n + 1}`"
|
||||
:limit="1"
|
||||
accept=".docx, application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
@error="uploadErrorHandle"
|
||||
@before-upload="confirmUploadHandle"
|
||||
>
|
||||
<template #upload-button>
|
||||
<a-button type="primary"><icon-upload />点击上传</a-button>
|
||||
</template>
|
||||
</a-upload>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
/* 该组件是回归测试说明、回归测试记录上传逻辑组件 */
|
||||
import { onMounted, ref } from "vue"
|
||||
import otherApi from "@/api/generate/other"
|
||||
import { Message, Modal } from "@arco-design/web-vue"
|
||||
|
||||
// props
|
||||
const { projectId, documentType } = defineProps<{ projectId: number | null; documentType: string }>()
|
||||
|
||||
// 组件加载时候就请求后端,有几个轮次
|
||||
onMounted(async () => {
|
||||
try {
|
||||
if (!projectId) return
|
||||
const {
|
||||
data: { count }
|
||||
} = await otherApi.getHgRoundNumber({ id: projectId })
|
||||
// count为非第一轮其他轮次数量
|
||||
roundNum.value = count
|
||||
} catch (err) {
|
||||
roundNum.value = 0
|
||||
}
|
||||
})
|
||||
|
||||
// ref - 定义非第一轮轮次数量
|
||||
const roundNum = ref(0)
|
||||
|
||||
// 上传失败的事件 - 报错内容由后端定义(message字段)
|
||||
const uploadErrorHandle = (fielItem: any) => {
|
||||
const res = JSON.parse(fielItem.response)
|
||||
if (res.message) {
|
||||
Message.error(res.message)
|
||||
}
|
||||
}
|
||||
// 上传弹窗事件
|
||||
const confirmUploadHandle = (file: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: "请确认您上传的文件是带有文档片段的docx文档",
|
||||
content: `${file.name}`,
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => reject("cancel")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: "HgDocUpload"
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
283
cdTMP/src/views/testmanage/projmanage/GeneratorModal/index.vue
Normal file
283
cdTMP/src/views/testmanage/projmanage/GeneratorModal/index.vue
Normal file
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<!-- 该组件是点击生成的弹出框 -->
|
||||
<div class="create-modal-container">
|
||||
<a-modal v-model:visible="visible" unmount-on-close render-to-body width="50%" hide-cancel :footer="false">
|
||||
<template #title> {{ title }}上传下载 </template>
|
||||
<a-list class="alist">
|
||||
<template #header>上传您的{{ title }}:</template>
|
||||
<a-list-item>
|
||||
<!-- 文件上传:回归测试说明、回归测试记录 -->
|
||||
<div v-if="title === '回归测试说明' || title === '回归测试记录'">
|
||||
<HgDocUpload :project-id="projectId" :document-type="title" />
|
||||
</div>
|
||||
<!-- 文件上传:其他文档 -->
|
||||
<div v-else>
|
||||
<a-upload
|
||||
:action="`/api/documentUpload/file?id=${projectId}&documentType=${title}`"
|
||||
:limit="1"
|
||||
accept=".docx, application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
@error="uploadErrorHandle"
|
||||
@before-upload="confirmUploadHandle"
|
||||
/>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
<!-- 文档片段覆盖选取 -->
|
||||
<div v-if="fragmentList.length">
|
||||
<a-list :loading="fragmentListPending" hoverable size="small">
|
||||
<template #header>选取需要被覆盖的文档片段:</template>
|
||||
<a-list-item>
|
||||
<div class="buttonAndAlert">
|
||||
<a-alert type="warning">首次生成请全勾选</a-alert>
|
||||
<div class="button-list">
|
||||
<a-button type="primary" status="success" @click="allCheckBtn">全勾选</a-button>
|
||||
<a-button type="primary" status="warning" @click="allUnCheckBtn">全不勾选</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</a-list-item>
|
||||
<a-list-item v-for="(frag, index) in fragmentList" :key="index">
|
||||
<div class="fragment-item">
|
||||
<div class="fragment-name">{{ frag.name }}</div>
|
||||
<a-divider direction="vertical" />
|
||||
<a-switch v-model="frag.isCover"></a-switch>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</div>
|
||||
<div v-else>
|
||||
<a-list :loading="fragmentListPending" hoverable size="small">
|
||||
<template #header>选取需要被覆盖的文档片段:</template>
|
||||
<a-list-item>
|
||||
<Empty text="未找到文档片段,请先下载" />
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</div>
|
||||
<div>
|
||||
<a-list class="alist">
|
||||
<a-list-item>
|
||||
<div class="button-list">
|
||||
<a-button :loading="isGenerating" type="primary" @click="downloadHandle">
|
||||
确认并下载
|
||||
</a-button>
|
||||
</div>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
</div>
|
||||
</a-modal>
|
||||
<Progress
|
||||
:visible="progressVisible"
|
||||
:isComplete="isComplete"
|
||||
:text="title"
|
||||
@clickConfirm="handleModalConfirmClick"
|
||||
></Progress>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DocumentType } from "@/utils/types/CommonType" // 产品文档类型
|
||||
import { ref } from "vue"
|
||||
import fragmentApi from "@/api/generate/fragment" // 获取某项目某测试文档的片段api
|
||||
import Empty from "@/components/Empty/index.vue" // 空状态组件
|
||||
import HgDocUpload from "@/views/testmanage/projmanage/GeneratorModal/HgDocUpload/index.vue"
|
||||
import Progress from "@/views/testmanage/projmanage/cpns/progress.vue"
|
||||
import { Message, Modal } from "@arco-design/web-vue"
|
||||
import useGenerateSecond from "../hooks/useGenerateSecond"
|
||||
import useSeitaiModal from "../hooks/useSeitaiModal"
|
||||
|
||||
// 定义覆盖文档片段每项类型
|
||||
export interface IFragmentItem {
|
||||
name: string
|
||||
isCover: boolean
|
||||
}
|
||||
|
||||
// ~~~~1.文档片段展示功能~~~~
|
||||
const visible = ref(false) // v-model:显式隐藏modal
|
||||
const title = ref("测评大纲") // modal的标题
|
||||
const projectId = ref<number | null>(null) // 传入的项目id
|
||||
const fragmentListPending = ref(false) // 片段列表请求的loading状态
|
||||
// 定义文档片段储存
|
||||
const fragmentList = ref<IFragmentItem[]>([])
|
||||
|
||||
// out:暴露出去的函数
|
||||
const open = async (documentType: DocumentType, id: number) => {
|
||||
projectId.value = id // 传入的项目id
|
||||
title.value = documentType
|
||||
visible.value = true
|
||||
// 打开时清空fragmentList数据
|
||||
fragmentList.value = []
|
||||
// 打开时请求数据
|
||||
fragmentListPending.value = true // 设置loading状态
|
||||
// 请求片段列表数据
|
||||
try {
|
||||
const { data } = await fragmentApi.getFragmentByDocumentType({
|
||||
id: projectId.value,
|
||||
documentType
|
||||
})
|
||||
// 填充到fragmentList
|
||||
fragmentList.value = data.map((it: string) => ({
|
||||
name: it,
|
||||
isCover: false
|
||||
}))
|
||||
fragmentListPending.value = false
|
||||
} catch (err) {
|
||||
fragmentListPending.value = false // 请求失败,关闭loading状态
|
||||
}
|
||||
}
|
||||
|
||||
// 全勾选/全不勾选按钮
|
||||
const allCheckBtn = () => {
|
||||
fragmentList.value.forEach((item) => {
|
||||
item.isCover = true // 全部勾选
|
||||
})
|
||||
}
|
||||
const allUnCheckBtn = () => {
|
||||
fragmentList.value.forEach((item) => {
|
||||
item.isCover = false // 全部不勾选
|
||||
})
|
||||
}
|
||||
|
||||
// ~~~~2.文件上传功能~~~~
|
||||
// 上传失败的事件 - 报错内容由后端定义(message字段)
|
||||
const uploadErrorHandle = (fielItem: any) => {
|
||||
const res = JSON.parse(fielItem.response)
|
||||
if (res.message) {
|
||||
Message.error(res.message)
|
||||
}
|
||||
}
|
||||
// 上传弹窗事件
|
||||
const confirmUploadHandle = (file: any) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
Modal.confirm({
|
||||
title: "请确认您上传的文件是带有文档片段的docx文档",
|
||||
content: `${file.name}`,
|
||||
onOk: () => resolve(true),
|
||||
onCancel: () => reject("cancel")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ~~~~3.产品文档下载功能~~~~
|
||||
// 注意二段文档生成成功后,需要刷新片段列表数据(待完成)
|
||||
const downloadHandle = async () => {
|
||||
// 判断产品文档类型
|
||||
const documentType = title.value
|
||||
try {
|
||||
// 二段文档异步请求
|
||||
switch (documentType) {
|
||||
case "测评大纲":
|
||||
await createDgItem(projectId.value!)
|
||||
break
|
||||
case "测试说明":
|
||||
await createSmItem(projectId.value!)
|
||||
break
|
||||
case "测试记录":
|
||||
await createJLItem(projectId.value!)
|
||||
break
|
||||
case "回归测试说明":
|
||||
await createHsmItem(projectId.value!)
|
||||
break
|
||||
case "回归测试记录":
|
||||
await createHjlItem(projectId.value!)
|
||||
break
|
||||
case "问题单":
|
||||
await createWtdItem(projectId.value!)
|
||||
break
|
||||
case "测评报告":
|
||||
await createBgItem(projectId.value!)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
// 生成最终产品文档请求 -> 添加当前用户取消选择的片段
|
||||
switch (documentType) {
|
||||
case "测评大纲":
|
||||
await createSeitaiDagang(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
case "测试说明":
|
||||
await createSeitaiShuoming(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
case "测试记录":
|
||||
await createSeitaiJilu(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
case "回归测试说明":
|
||||
await createSeitaiHsm(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
case "回归测试记录":
|
||||
await createSeitaiHjl(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
case "问题单":
|
||||
await createSeitaiWtd(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
case "测评报告":
|
||||
await createSeitaiBaogao(projectId.value!, documentType, fragmentList.value)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
// 为了方便直接关闭弹窗,这样用户可以使用open函数初始化
|
||||
visible.value = false
|
||||
} catch (err) {
|
||||
// 总体出错处理,关闭弹窗
|
||||
visible.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// hook-二段文档函数
|
||||
const {
|
||||
isGenerating,
|
||||
createDgItem,
|
||||
createSmItem,
|
||||
createJLItem,
|
||||
createHsmItem,
|
||||
createHjlItem,
|
||||
createWtdItem,
|
||||
createBgItem
|
||||
} = useGenerateSecond()
|
||||
|
||||
// hook-生成产品文档
|
||||
const {
|
||||
visible: progressVisible,
|
||||
isComplete,
|
||||
handleModalConfirmClick,
|
||||
createSeitaiDagang,
|
||||
createSeitaiShuoming,
|
||||
createSeitaiJilu,
|
||||
createSeitaiHsm,
|
||||
createSeitaiHjl,
|
||||
createSeitaiWtd,
|
||||
createSeitaiBaogao
|
||||
} = useSeitaiModal()
|
||||
|
||||
defineExpose({ open })
|
||||
|
||||
defineOptions({
|
||||
name: "GeneratorModal"
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.fragment-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
.fragment-name {
|
||||
width: 40%;
|
||||
}
|
||||
}
|
||||
|
||||
.button-list {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.alist {
|
||||
margin: 10px 0;
|
||||
}
|
||||
.buttonAndAlert {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
@@ -4,23 +4,31 @@ export default {
|
||||
/**
|
||||
* 生成最终产品文档的进度条模块
|
||||
*/
|
||||
async create_entire_doc(visible, isComplete, api, record_id, docName) {
|
||||
async create_entire_doc(visible, isComplete, api, record_id, docName, fragmentList) {
|
||||
visible.value = true
|
||||
isComplete.value = false
|
||||
const st = await api({ id: record_id }).catch((err) => {
|
||||
isComplete.value = true
|
||||
visible.value = false
|
||||
})
|
||||
try {
|
||||
const st = await api({ id: record_id, frag: fragmentList })
|
||||
// 动态生成文件名
|
||||
const fileType = st.type
|
||||
const fileExt = fileType.includes("zip") ? "zip" : "docx"
|
||||
const fileName = `${docName}.${fileExt}`
|
||||
// 下面是创建后Blob并触发下载
|
||||
const blob = new Blob([st], { type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document" })
|
||||
const blob = new Blob([st], {
|
||||
type: fileType
|
||||
})
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const a = document.createElement("a")
|
||||
a.href = url
|
||||
a.download = `${docName}.docx` // 设置下载文件名
|
||||
a.download = fileName // 设置文件名(动态)
|
||||
a.click()
|
||||
window.URL.revokeObjectURL(url) // 释放 URL 对象
|
||||
// 上面是触发下载
|
||||
isComplete.value = true
|
||||
Message.success("文档生成并下载成功!")
|
||||
} catch (err) {
|
||||
isComplete.value = true
|
||||
visible.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ const useCrudInit = function () {
|
||||
rowSelection: { showCheckedAll: true },
|
||||
api: projectApi.getPageList,
|
||||
add: { show: true, api: projectApi.save, text: "新增项目" },
|
||||
edit: { show: true, api: projectApi.update, text: "编辑项目" }, // auth未空数组则所有都可以
|
||||
edit: { show: true, api: projectApi.update, text: "编辑" }, // auth未空数组则所有都可以
|
||||
delete: { show: true, api: projectApi.delete },
|
||||
searchColNumber: 3,
|
||||
tablePagination: false,
|
||||
operationColumn: true,
|
||||
operationWidth: 500,
|
||||
showIndex: false,
|
||||
operationColumnWidth: 280, // 操作列宽度
|
||||
operationColumnWidth: 400, // 操作列宽度
|
||||
operationColumnAlign: "center", // 操作列对齐方式
|
||||
afterDelete(response: any) {
|
||||
crudRef.value.tableRef.selectAll(false)
|
||||
@@ -37,17 +37,8 @@ const useCrudInit = function () {
|
||||
{
|
||||
formType: "grid",
|
||||
cols: [
|
||||
{ span: 8, formList: [{ dataIndex: "ident" }] },
|
||||
{ span: 8, formList: [{ dataIndex: "name" }] },
|
||||
{ span: 8, formList: [{ dataIndex: "engin_model" }] }
|
||||
]
|
||||
},
|
||||
{
|
||||
formType: "grid",
|
||||
cols: [
|
||||
{ span: 8, formList: [{ dataIndex: "section_system" }] },
|
||||
{ span: 8, formList: [{ dataIndex: "sub_system" }] },
|
||||
{ span: 8, formList: [{ dataIndex: "device" }] }
|
||||
{ span: 4, formList: [{ dataIndex: "ident" }] },
|
||||
{ span: 8, formList: [{ dataIndex: "name" }] }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -150,7 +141,7 @@ const useCrudInit = function () {
|
||||
{
|
||||
title: "项目标识",
|
||||
align: "center",
|
||||
width: 90,
|
||||
width: 80,
|
||||
sortable: { sortDirections: ["ascend"] },
|
||||
dataIndex: "ident",
|
||||
search: true,
|
||||
@@ -163,28 +154,26 @@ const useCrudInit = function () {
|
||||
},
|
||||
{
|
||||
title: "项目名称",
|
||||
width: 110,
|
||||
width: 120,
|
||||
align: "center",
|
||||
dataIndex: "name",
|
||||
search: true,
|
||||
commonRules: [{ required: true, message: "名称是必填" }]
|
||||
},
|
||||
{ title: "工程型号", dataIndex: "engin_model", hide: true },
|
||||
{ title: "分系统", dataIndex: "section_system", hide: true },
|
||||
{ title: "子系统", dataIndex: "sub_system", hide: true },
|
||||
{ title: "设备名称", dataIndex: "device", hide: true },
|
||||
{
|
||||
title: "开始日期",
|
||||
dataIndex: "beginTime",
|
||||
align: "center",
|
||||
commonRules: [{ required: true, message: "开始时间必填" }],
|
||||
formType: "date"
|
||||
formType: "date",
|
||||
width: 110
|
||||
},
|
||||
{
|
||||
title: "结束时间",
|
||||
align: "center",
|
||||
dataIndex: "endTime",
|
||||
formType: "date",
|
||||
width: 110,
|
||||
extra: "注意:结束时间需要晚于最后一轮结束时间",
|
||||
commonRules: [
|
||||
{
|
||||
@@ -212,7 +201,7 @@ const useCrudInit = function () {
|
||||
{
|
||||
title: "责任人",
|
||||
align: "center",
|
||||
width: 70,
|
||||
width: 50,
|
||||
dataIndex: "duty_person",
|
||||
search: true,
|
||||
commonRules: [{ required: true, message: "责任人必选" }],
|
||||
@@ -352,6 +341,7 @@ const useCrudInit = function () {
|
||||
align: "center",
|
||||
addDefaultValue: "9",
|
||||
search: true,
|
||||
width: 70,
|
||||
commonRules: [{ required: true, message: "报告类型必填" }],
|
||||
// 字典-report_type
|
||||
formType: "radio",
|
||||
@@ -371,7 +361,7 @@ const useCrudInit = function () {
|
||||
{
|
||||
title: "依据标准",
|
||||
dataIndex: "standard",
|
||||
addDefaultValue: ["1", "2", "3", "4", "9"],
|
||||
addDefaultValue: ["16", "2", "17", "3", "7", "4", "5", "6"],
|
||||
maxTagCount: 20,
|
||||
commonRules: [{ required: true, message: "请至少选择一个" }],
|
||||
hide: true,
|
||||
@@ -510,6 +500,7 @@ const useCrudInit = function () {
|
||||
{
|
||||
title: "状态",
|
||||
align: "center",
|
||||
width: 80,
|
||||
dataIndex: "step",
|
||||
search: true,
|
||||
formType: "radio",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* 该文件是配合请求后端生成各文档的二段文档
|
||||
*/
|
||||
import { ref } from "vue"
|
||||
import dgGenerateApi from "@/api/generate/dgGenerate"
|
||||
import smGenerateApi from "@/api/generate/smGenerate"
|
||||
@@ -6,7 +9,6 @@ import bgGenerateApi from "@/api/generate/bgGenerate"
|
||||
import hsmGenerateApi from "@/api/generate/hsmGenerate"
|
||||
import hjlGenerateApi from "@/api/generate/hjlGenerate"
|
||||
import wtdGenerateApi from "@/api/generate/wtdGenerate"
|
||||
import { Message } from "@arco-design/web-vue"
|
||||
|
||||
const useGenerateSecond = function () {
|
||||
// refs
|
||||
@@ -20,53 +22,10 @@ const useGenerateSecond = function () {
|
||||
const ishjlLoading = ref(false)
|
||||
const isWtdLoading = ref(false)
|
||||
// events
|
||||
// 记录生成二级文档
|
||||
const createJLItem = async (record: any) => {
|
||||
isGenerating.value = true
|
||||
isJlloading.value = true
|
||||
await jlGenerateApi.createJLcaserecord({ id: record.id }).finally(() => {
|
||||
isGenerating.value = false
|
||||
isJlloading.value = false
|
||||
})
|
||||
Message.success("记录-片段库生成成功,请查看output/jl文件夹")
|
||||
}
|
||||
// 说明生成二级文档
|
||||
const createSmItem = async (record: any) => {
|
||||
isGenerating.value = true
|
||||
isSmLoading.value = true
|
||||
const id = record.id
|
||||
await Promise.all([
|
||||
dgGenerateApi.createSoftComposition({ id }), // 生成测评对象 - 和大纲一样
|
||||
dgGenerateApi.createFuncList({ id }), // 生成被测软件功能 - 和大纲重复
|
||||
dgGenerateApi.createInterface({ id }), // 生成被测软件接口 - 和大纲重复 - 可能会删除
|
||||
dgGenerateApi.createPerformance({ id }), // 生成被测软件性能 - 和大纲重复 - 可能会删除
|
||||
dgGenerateApi.createBaseInformation({ id }), // 生成被测软件基本信息 - 和大纲重复 - 可能会删除
|
||||
dgGenerateApi.createYiju({ id }), // 生成标准类引用文档 - 和大纲重复 - 可能会删除
|
||||
smGenerateApi.createSMTechyiju({ id }), // 生成技术类引用文档列表 -> 在大纲基础上添加《测评大纲》
|
||||
// 拆分软硬件环境
|
||||
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
|
||||
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
|
||||
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
|
||||
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
|
||||
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
|
||||
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
|
||||
dgGenerateApi.createTestData({ id }), // 生成-测评数据
|
||||
dgGenerateApi.createEnvDiff({ id }), // 生成-环境差异性分析
|
||||
// ~~~
|
||||
smGenerateApi.createSMCaseList({ id }), // 生成用例全
|
||||
smGenerateApi.createSMCaseBreifList({ id }), // 生成用例列表-那个表格
|
||||
smGenerateApi.createSMTrack({ id }) // 生成说明追踪
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
isSmLoading.value = false
|
||||
})
|
||||
Message.success("说明-片段库生成成功,请查看output/sm文件夹")
|
||||
}
|
||||
// 大纲生成二级文档
|
||||
const createDgItem = async (e: any, record: any) => {
|
||||
const createDgItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
isDgLoading.value = true
|
||||
const id = record.id
|
||||
await Promise.all([
|
||||
dgGenerateApi.createTestDemand({ id }), // 生成第一轮测试项
|
||||
dgGenerateApi.createYiju({ id }), // 生成依据文件
|
||||
@@ -97,19 +56,104 @@ const useGenerateSecond = function () {
|
||||
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
|
||||
dgGenerateApi.createTestData({ id }), // 生成-测评数据
|
||||
dgGenerateApi.createEnvDiff({ id }), // 生成-环境差异性分析
|
||||
// ~~~~~~~~~
|
||||
dgGenerateApi.createMainTech({ id }) // 生成-主要战技指标
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
isDgLoading.value = false
|
||||
})
|
||||
Message.success("大纲-片段库文档生成成功,请查看output/dg文件夹")
|
||||
}
|
||||
// 说明生成二级文档
|
||||
const createSmItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
isSmLoading.value = true
|
||||
await Promise.all([
|
||||
dgGenerateApi.createSoftComposition({ id }), // 生成测评对象 - 和大纲一样
|
||||
dgGenerateApi.createFuncList({ id }), // 生成被测软件功能 - 和大纲重复
|
||||
dgGenerateApi.createInterface({ id }), // 生成被测软件接口 - 和大纲重复 - 可能会删除
|
||||
dgGenerateApi.createPerformance({ id }), // 生成被测软件性能 - 和大纲重复 - 可能会删除
|
||||
dgGenerateApi.createBaseInformation({ id }), // 生成被测软件基本信息 - 和大纲重复 - 可能会删除
|
||||
dgGenerateApi.createYiju({ id }), // 生成标准类引用文档 - 和大纲重复 - 可能会删除
|
||||
smGenerateApi.createSMTechyiju({ id }), // 生成技术类引用文档列表 -> 在大纲基础上添加《测评大纲》
|
||||
// 拆分软硬件环境
|
||||
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
|
||||
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
|
||||
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
|
||||
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
|
||||
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
|
||||
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
|
||||
dgGenerateApi.createTestData({ id }), // 生成-测评数据
|
||||
dgGenerateApi.createEnvDiff({ id }), // 生成-环境差异性分析
|
||||
// ~~~
|
||||
smGenerateApi.createSMCaseList({ id }), // 生成用例全
|
||||
smGenerateApi.createSMCaseBreifList({ id }), // 生成用例列表-那个表格
|
||||
smGenerateApi.createSMTrack({ id }) // 生成说明追踪
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
isSmLoading.value = false
|
||||
})
|
||||
}
|
||||
// 记录生成二级文档
|
||||
const createJLItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
isJlloading.value = true
|
||||
await jlGenerateApi.createJLcaserecord({ id }).finally(() => {
|
||||
isGenerating.value = false
|
||||
isJlloading.value = false
|
||||
})
|
||||
}
|
||||
// 回归测试说明二级文档
|
||||
const createHsmItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
ishsmLoading.value = true
|
||||
await Promise.all([
|
||||
hsmGenerateApi.deleteHSMFiles({ id }), // 先删除以前文件
|
||||
hsmGenerateApi.createBasicInfo({ id }),
|
||||
hsmGenerateApi.createDocSummary({ id }),
|
||||
hsmGenerateApi.createJstech({ id }),
|
||||
hsmGenerateApi.createChangePart({ id }),
|
||||
hsmGenerateApi.createHdemand({ id }),
|
||||
hsmGenerateApi.createCaseListDesc({ id }),
|
||||
hsmGenerateApi.createCaseList({ id }),
|
||||
hsmGenerateApi.createTrack({ id }),
|
||||
// 拆分大纲软硬件环境
|
||||
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
|
||||
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
|
||||
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
|
||||
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
|
||||
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
|
||||
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
|
||||
dgGenerateApi.createTestData({ id }), // 生成-测评数据
|
||||
dgGenerateApi.createEnvDiff({ id }) // 生成-环境差异性分析
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
ishsmLoading.value = false
|
||||
})
|
||||
}
|
||||
// 回归测试记录二级文档
|
||||
const createHjlItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
ishjlLoading.value = true
|
||||
await Promise.all([
|
||||
hjlGenerateApi.deleteHJLFiles({ id }), // 先调用删除文件夹里面文件
|
||||
hjlGenerateApi.createBasicInfo({ id }),
|
||||
hjlGenerateApi.createCaseinfo({ id })
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
ishjlLoading.value = false
|
||||
})
|
||||
}
|
||||
// 问题单二级文档
|
||||
const createWtdItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
isWtdLoading.value = true
|
||||
await wtdGenerateApi.createWtdTable({ id }).finally(() => {
|
||||
isGenerating.value = false
|
||||
isWtdLoading.value = false
|
||||
})
|
||||
}
|
||||
// 报告生成二级文档
|
||||
const createBgItem = async (record: any) => {
|
||||
const createBgItem = async (id: number) => {
|
||||
isGenerating.value = true
|
||||
isBgLoading.value = true
|
||||
const id = record.id
|
||||
await Promise.all([
|
||||
bgGenerateApi.deleteBGFiles({ id }), // 删除output/bg文件夹下文件
|
||||
bgGenerateApi.createBgTechYiju({ id }),
|
||||
@@ -138,64 +182,7 @@ const useGenerateSecond = function () {
|
||||
isGenerating.value = false
|
||||
isBgLoading.value = false
|
||||
})
|
||||
Message.success("报告-片段库文档生成成功,请查看output/bg文件夹")
|
||||
}
|
||||
// 回归测试说明二级文档
|
||||
const createHsmItem = async (record: any) => {
|
||||
const id = record.id
|
||||
isGenerating.value = true
|
||||
ishsmLoading.value = true
|
||||
await Promise.all([
|
||||
hsmGenerateApi.deleteHSMFiles({ id }), // 先删除以前文件
|
||||
hsmGenerateApi.createBasicInfo({ id }),
|
||||
hsmGenerateApi.createDocSummary({ id }),
|
||||
hsmGenerateApi.createJstech({ id }),
|
||||
hsmGenerateApi.createChangePart({ id }),
|
||||
hsmGenerateApi.createHdemand({ id }),
|
||||
hsmGenerateApi.createCaseListDesc({ id }),
|
||||
hsmGenerateApi.createCaseList({ id }),
|
||||
hsmGenerateApi.createTrack({ id }),
|
||||
// 拆分大纲软硬件环境
|
||||
dgGenerateApi.createStaticEnvironment({ id }), // 生成-静态测试环境说明
|
||||
dgGenerateApi.createStaticSoft({ id }), // 生成-静态软件项
|
||||
dgGenerateApi.createStaticHard({ id }), // 生成-静态硬件和固件项
|
||||
dgGenerateApi.createDynamicEnv({ id }), // 生成-动态测试环境说明
|
||||
dgGenerateApi.createDynamicSoft({ id }), // 生成-动态软件项
|
||||
dgGenerateApi.createDynamicHard({ id }), // 生成-动态硬件和固件项
|
||||
dgGenerateApi.createTestData({ id }), // 生成-测评数据
|
||||
dgGenerateApi.createEnvDiff({ id }) // 生成-环境差异性分析
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
ishsmLoading.value = false
|
||||
})
|
||||
Message.success("回归说明-片段库文档生成成功,请查看output/hsm文件夹")
|
||||
}
|
||||
// 回归测试记录二级文档
|
||||
const createHjlItem = async (record: any) => {
|
||||
const id = record.id
|
||||
isGenerating.value = true
|
||||
ishjlLoading.value = true
|
||||
await Promise.all([
|
||||
hjlGenerateApi.deleteHJLFiles({ id }), // 先调用删除文件夹里面文件
|
||||
hjlGenerateApi.createBasicInfo({ id }),
|
||||
hjlGenerateApi.createCaseinfo({ id })
|
||||
]).finally(() => {
|
||||
isGenerating.value = false
|
||||
ishjlLoading.value = false
|
||||
})
|
||||
Message.success("回归记录-片段库文档生成成功,请查看output/hjl文件夹")
|
||||
}
|
||||
// 问题单二级文档
|
||||
const createWtdItem = async (record: any) => {
|
||||
isGenerating.value = true
|
||||
isWtdLoading.value = true
|
||||
await wtdGenerateApi.createWtdTable({ id: record.id }).finally(() => {
|
||||
isGenerating.value = false
|
||||
isWtdLoading.value = false
|
||||
})
|
||||
Message.success("问题单-片段库文档生成成功,请查看output/wtd文件夹")
|
||||
}
|
||||
|
||||
return {
|
||||
isGenerating,
|
||||
isDgLoading,
|
||||
|
||||
@@ -1,69 +1,61 @@
|
||||
/**
|
||||
* 生成产品文档按钮
|
||||
*/
|
||||
import { ref } from "vue"
|
||||
import hoosk from "./hooks"
|
||||
import seitaiGenerateApi from "@/api/generate/seitaiGenerate"
|
||||
import type { IFragmentItem } from "@/views/testmanage/projmanage/GeneratorModal/index.vue"
|
||||
|
||||
const useSeitaiModal = function () {
|
||||
// refs
|
||||
const visible = ref(false)
|
||||
const isComplete = ref(false)
|
||||
const ptext = ref("测评大纲")
|
||||
const visible = ref(false) // Modal显隐
|
||||
const isComplete = ref(false) // 是否生成完成
|
||||
// events
|
||||
const handleModalConfirmClick = () => {
|
||||
visible.value = false
|
||||
visible.value = false // 关闭Modal
|
||||
}
|
||||
// 生成文档
|
||||
// ~~~~~~~~测试说明生成文档~~~~~~~~
|
||||
const createSeitaiShuoming = async (record: any) => {
|
||||
ptext.value = "测试说明"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createShuomingSeiTai, record.id, ptext.value)
|
||||
}
|
||||
|
||||
// ~~~~~~~~测试大纲生成文档~~~~~~~~
|
||||
const createSeitaiDagang = async (record: any) => {
|
||||
const createSeitaiDagang = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
// 根据一系列文档生成大纲 - 这里有进度条组件、a-modal组件
|
||||
// 1.打开进度条组件
|
||||
ptext.value = "测评大纲"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createDagangSeiTai, record.id, ptext.value)
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createDagangSeiTai, id, ptext, fragmentList)
|
||||
}
|
||||
// ~~~~~~~~测试说明生成文档~~~~~~~~
|
||||
const createSeitaiShuoming = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createShuomingSeiTai, id, ptext, fragmentList)
|
||||
}
|
||||
// ~~~~~~~~记录生成文档~~~~~~~~
|
||||
const createSeitaiJilu = async (record: any) => {
|
||||
ptext.value = "测试记录"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createJiluSeiTai, record.id, ptext.value)
|
||||
}
|
||||
|
||||
// ~~~~~~~~报告生成文档~~~~~~~~
|
||||
const createSeitaiBaogao = async (record: any) => {
|
||||
ptext.value = "测评报告"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createBgDocument, record.id, ptext.value)
|
||||
const createSeitaiJilu = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createJiluSeiTai, id, ptext, fragmentList)
|
||||
}
|
||||
// ~~~~~~~~回归测试说明~~~~~~~~
|
||||
const createSeitaiHsm = async (record: any) => {
|
||||
ptext.value = "回归测试说明"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createHsmDocument, record.id, ptext.value)
|
||||
const createSeitaiHsm = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createHsmDocument, id, ptext, fragmentList)
|
||||
}
|
||||
// ~~~~~~~~回归测试记录~~~~~~~~
|
||||
const createSeitaiHjl = async (record: any) => {
|
||||
ptext.value = "回归测试记录"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createHjlDocument, record.id, ptext.value)
|
||||
const createSeitaiHjl = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createHjlDocument, id, ptext, fragmentList)
|
||||
}
|
||||
// ~~~~~~~~问题单~~~~~~~~
|
||||
const createSeitaiWtd = async (record: any) => {
|
||||
ptext.value = "问题单"
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createWtdDocument, record.id, ptext.value)
|
||||
const createSeitaiWtd = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createWtdDocument, id, ptext, fragmentList)
|
||||
}
|
||||
// ~~~~~~~~报告生成文档~~~~~~~~
|
||||
const createSeitaiBaogao = async (id: number, ptext: string, fragmentList: IFragmentItem[]) => {
|
||||
hoosk.create_entire_doc(visible, isComplete, seitaiGenerateApi.createBgDocument, id, ptext, fragmentList)
|
||||
}
|
||||
|
||||
return {
|
||||
visible,
|
||||
isComplete,
|
||||
ptext,
|
||||
handleModalConfirmClick,
|
||||
createSeitaiShuoming,
|
||||
createSeitaiDagang,
|
||||
createSeitaiShuoming,
|
||||
createSeitaiJilu,
|
||||
createSeitaiBaogao,
|
||||
createSeitaiHsm,
|
||||
createSeitaiHjl,
|
||||
createSeitaiWtd
|
||||
createSeitaiWtd,
|
||||
createSeitaiBaogao
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,89 +5,25 @@
|
||||
<!-- ma-crud组件 -->
|
||||
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef">
|
||||
<template #operationBeforeExtend="{ record }">
|
||||
<a-popover title="文档生成组合按钮" trigger="click">
|
||||
<a-popover title="点击配置生成文档" trigger="click">
|
||||
<a-button type="primary" size="mini">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
<icon-download />
|
||||
</template>
|
||||
文档生成
|
||||
</a-button>
|
||||
<template #content>
|
||||
<p>
|
||||
<a-link
|
||||
:disabled="isGenerating"
|
||||
:loading="isDgLoading"
|
||||
@click="createDgItem($event, record)"
|
||||
>
|
||||
大纲二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" :loading="isSmLoading" @click="createSmItem(record)">
|
||||
说明二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" :loading="isJlloading" @click="createJLItem(record)">
|
||||
记录二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" :loading="isBgLoading" @click="createBgItem(record)">
|
||||
报告二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" :loading="ishsmLoading" @click="createHsmItem(record)">
|
||||
回归说明二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" :loading="ishjlLoading" @click="createHjlItem(record)">
|
||||
回归记录二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" :loading="isWtdLoading" @click="createWtdItem(record)">
|
||||
问题单二段文档
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiDagang(record)">
|
||||
<icon-eye />
|
||||
[测试]生成最后大纲
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiShuoming(record)">
|
||||
<icon-eye />[测试]生成最后说明
|
||||
</a-link>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiJilu(record)"
|
||||
><icon-eye />[测试]生成最后记录</a-link
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiBaogao(record)"
|
||||
><icon-eye />[测试]生成测评报告</a-link
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiHsm(record)"
|
||||
><icon-eye />[测试]回归测试说明</a-link
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiHjl(record)"
|
||||
><icon-eye />[测试]回归测试记录</a-link
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a-link :disabled="isGenerating" @click="createSeitaiWtd(record)"
|
||||
><icon-eye />[测试]生成问题单</a-link
|
||||
>
|
||||
</p>
|
||||
<a-space direction="vertical" :size="0" align="start">
|
||||
<a-space direction="vertical" :size="0" align="start">
|
||||
<a-link @click="openCreateModal('测评大纲', record.id)">测评大纲</a-link>
|
||||
<a-link @click="openCreateModal('测试说明', record.id)">测试说明</a-link>
|
||||
<a-link @click="openCreateModal('测试记录', record.id)">测试记录</a-link>
|
||||
<a-link @click="openCreateModal('回归测试说明', record.id)">回归测试说明</a-link>
|
||||
<a-link @click="openCreateModal('回归测试记录', record.id)">回归测试记录</a-link>
|
||||
<a-link @click="openCreateModal('问题单', record.id)">问题单</a-link>
|
||||
<a-link @click="openCreateModal('测评报告', record.id)">测评报告</a-link>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-popover>
|
||||
<a-button @click="enterWorkPlant(record)" size="mini" status="warning" type="outline">
|
||||
@@ -95,70 +31,33 @@
|
||||
</a-button>
|
||||
<a-link @click="previewRef.open(record)"><icon-eye />预览</a-link>
|
||||
<a-link @click="handleFragmentClick(record)"><icon-file />片段</a-link>
|
||||
<a-link @click="handleBoardClick(record)"><icon-dashboard />项目看板</a-link>
|
||||
<a-link @click="handleBoardClick(record)"><icon-dashboard />看板</a-link>
|
||||
</template>
|
||||
</ma-crud>
|
||||
<GeneratorModal ref="generatorModalRef" />
|
||||
</div>
|
||||
<preview ref="previewRef" :columns="crudColumns"></preview>
|
||||
<Progress
|
||||
:visible="visible"
|
||||
:isComplete="isComplete"
|
||||
:text="ptext"
|
||||
@clickConfirm="handleModalConfirmClick"
|
||||
></Progress>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="jsx">
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import { useRouter } from "vue-router"
|
||||
import preview from "./cpns/preview.vue"
|
||||
import Progress from "./cpns/progress.vue"
|
||||
import useEnterWorkPlant from "./hooks/useEnterWorkPlant"
|
||||
import useSeitaiModal from "./hooks/useSeitaiModal"
|
||||
import useGenerateSecond from "./hooks/useGenerateSecond"
|
||||
import preview from "./cpns/preview.vue" // 项目详情预览组件
|
||||
import useEnterWorkPlant from "./hooks/useEnterWorkPlant" // 进入工作区逻辑
|
||||
import useCrudInit from "./hooks/useCrudInit"
|
||||
import GeneratorModal from "./GeneratorModal/index.vue" // 生成文档Modal组件
|
||||
import type { DocumentType } from "@/utils/types/CommonType" // 产品文档类型
|
||||
|
||||
const router = useRouter()
|
||||
// crud配置和字段信息定义
|
||||
const { crudRef, crudOptions, crudColumns } = useCrudInit()
|
||||
// 点击进入工作区函数 - 每次点击后都清除localStorage中树状目录数据
|
||||
const { enterWorkPlant } = useEnterWorkPlant()
|
||||
// 生成最终文档事件,并且弹窗显隐和退出条件判断
|
||||
const {
|
||||
visible,
|
||||
isComplete,
|
||||
ptext,
|
||||
handleModalConfirmClick,
|
||||
createSeitaiShuoming,
|
||||
createSeitaiDagang,
|
||||
createSeitaiJilu,
|
||||
createSeitaiBaogao,
|
||||
createSeitaiHsm,
|
||||
createSeitaiHjl,
|
||||
createSeitaiWtd
|
||||
} = useSeitaiModal()
|
||||
// 用于生成二段文档按钮事件和禁用按钮ref
|
||||
const {
|
||||
isGenerating,
|
||||
isDgLoading,
|
||||
isSmLoading,
|
||||
isBgLoading,
|
||||
isJlloading,
|
||||
ishsmLoading,
|
||||
ishjlLoading,
|
||||
isWtdLoading,
|
||||
createJLItem,
|
||||
createSmItem,
|
||||
createDgItem,
|
||||
createBgItem,
|
||||
createHsmItem,
|
||||
createHjlItem,
|
||||
createWtdItem
|
||||
} = useGenerateSecond()
|
||||
|
||||
// 其他功能
|
||||
// 1.跳转到项目看板页面
|
||||
const handleBoardClick = (record) => {
|
||||
const handleBoardClick = (record: any) => {
|
||||
router.push({
|
||||
name: "projBoard",
|
||||
params: {
|
||||
@@ -167,7 +66,7 @@ const handleBoardClick = (record) => {
|
||||
})
|
||||
}
|
||||
// 2.跳转到项目所属文档片段
|
||||
const handleFragmentClick = (record) => {
|
||||
const handleFragmentClick = (record: any) => {
|
||||
router.push({
|
||||
name: "projFragment",
|
||||
params: {
|
||||
@@ -178,14 +77,19 @@ const handleFragmentClick = (record) => {
|
||||
// 3.预览项目信息ref
|
||||
const previewRef = ref()
|
||||
|
||||
// 4.生成文档组件
|
||||
const generatorModalRef = ref<InstanceType<typeof GeneratorModal> | null>(null) // GeneratorModal组件的ref
|
||||
const openCreateModal = (documentType: DocumentType, id: number) => {
|
||||
generatorModalRef.value!.open(documentType, id) // 打开弹出框
|
||||
}
|
||||
|
||||
defineOptions({
|
||||
name: "projmanage"
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
<style scoped lang="less">
|
||||
.msg-menu {
|
||||
// border-right: 1px solid var(--color-border-2);
|
||||
& li {
|
||||
border-radius: 1px;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"ES2015"
|
||||
],
|
||||
"types": [
|
||||
"vite/client"
|
||||
"vite/client",
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
Reference in New Issue
Block a user