日常修复内容

This commit is contained in:
2026-04-20 18:00:33 +08:00
parent d1b5fdcdd3
commit b9c971d6b5
16 changed files with 947 additions and 943 deletions

View File

@@ -1,19 +1,19 @@
{ {
"name": "testplant", "name": "testplant",
"version": "1.0.1", "version": "0.1.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "testplant", "name": "testplant",
"version": "1.0.1", "version": "0.1.1",
"dependencies": { "dependencies": {
"@arco-design/color": "^0.4.0", "@arco-design/color": "^0.4.0",
"@arco-design/web-vue": "^2.57.0", "@arco-design/web-vue": "^2.58.0",
"@tanstack/vue-query": "^5.99.0", "@tanstack/vue-query": "^5.99.2",
"@tinymce/tinymce-vue": "^6.3.0", "@tinymce/tinymce-vue": "^6.3.0",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.2.1",
"axios": "^1.15.0", "axios": "^1.15.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dayjs": "^1.11.20", "dayjs": "^1.11.20",
@@ -47,7 +47,7 @@
"@vitejs/plugin-vue-jsx": "^5.1.5", "@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/babel-plugin-jsx": "^2.0.1", "@vue/babel-plugin-jsx": "^2.0.1",
"browserslist": "^4.28.2", "browserslist": "^4.28.2",
"eslint": "^10.2.0", "eslint": "^10.2.1",
"eslint-plugin-vue": "^10.8.0", "eslint-plugin-vue": "^10.8.0",
"less": "^4.6.4", "less": "^4.6.4",
"less-loader": "^12.3.2", "less-loader": "^12.3.2",
@@ -55,7 +55,7 @@
"prettier": "^3.8.3", "prettier": "^3.8.3",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"tailwindcss": "^4.2.2", "tailwindcss": "^4.2.2",
"typescript": "^6.0.2", "typescript": "^6.0.3",
"vite": "^8.0.8", "vite": "^8.0.8",
"vue-eslint-parser": "^10.4.0" "vue-eslint-parser": "^10.4.0"
} }
@@ -83,9 +83,9 @@
} }
}, },
"node_modules/@arco-design/web-vue": { "node_modules/@arco-design/web-vue": {
"version": "2.57.0", "version": "2.58.0",
"resolved": "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.57.0.tgz", "resolved": "https://registry.npmmirror.com/@arco-design/web-vue/-/web-vue-2.58.0.tgz",
"integrity": "sha512-R5YReC3C2sG3Jv0+YuR3B7kzkq2KdhhQNCGXD8T11xAoa0zMt6SWTP1xJQOdZcM9du+q3z6tk5mRvh4qkieRJw==", "integrity": "sha512-b1vdPYOmjG5VAkVa7jlVwCb+WynBK+rnKN8zH3yKohpZObZbostRd3HgYNtjjZjGVU3OqR0Yy2FX7ftgF0bcOw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@arco-design/color": "^0.4.0", "@arco-design/color": "^0.4.0",
@@ -99,7 +99,7 @@
"vue": "^3.1.0" "vue": "^3.1.0"
}, },
"peerDependencies": { "peerDependencies": {
"vue": "^3.1.0" "vue": ">=3.1.0"
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
@@ -570,13 +570,13 @@
} }
}, },
"node_modules/@eslint/config-array": { "node_modules/@eslint/config-array": {
"version": "0.23.4", "version": "0.23.5",
"resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.23.4.tgz", "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.23.5.tgz",
"integrity": "sha512-lf19F24LSMfF8weXvW5QEtnLqW70u7kgit5e9PSx0MsHAFclGd1T9ynvWEMDT1w5J4Qt54tomGeAhdoAku1Xow==", "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/object-schema": "^3.0.4", "@eslint/object-schema": "^3.0.5",
"debug": "^4.3.1", "debug": "^4.3.1",
"minimatch": "^10.2.4" "minimatch": "^10.2.4"
}, },
@@ -585,22 +585,22 @@
} }
}, },
"node_modules/@eslint/config-helpers": { "node_modules/@eslint/config-helpers": {
"version": "0.5.4", "version": "0.5.5",
"resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.5.4.tgz", "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.5.5.tgz",
"integrity": "sha512-jJhqiY3wPMlWWO3370M86CPJ7pt8GmEwSLglMfQhjXal07RCvhmU0as4IuUEW5SJeunfItiEetHmSxCCe9lDBg==", "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^1.2.0" "@eslint/core": "^1.2.1"
}, },
"engines": { "engines": {
"node": "^20.19.0 || ^22.13.0 || >=24" "node": "^20.19.0 || ^22.13.0 || >=24"
} }
}, },
"node_modules/@eslint/core": { "node_modules/@eslint/core": {
"version": "1.2.0", "version": "1.2.1",
"resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.2.0.tgz", "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.2.1.tgz",
"integrity": "sha512-8FTGbNzTvmSlc4cZBaShkC6YvFMG0riksYWRFKXztqVdXaQbcZLXlFbSpC05s70sGEsXAw0qwhx69JiW7hQS7A==", "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
@@ -611,9 +611,9 @@
} }
}, },
"node_modules/@eslint/object-schema": { "node_modules/@eslint/object-schema": {
"version": "3.0.4", "version": "3.0.5",
"resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-3.0.4.tgz", "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-3.0.5.tgz",
"integrity": "sha512-55lO/7+Yp0ISKRP0PsPtNTeNGapXaO085aELZmWCVc5SH3jfrqpuU6YgOdIxMS99ZHkQN1cXKE+cdIqwww9ptw==", "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"engines": { "engines": {
@@ -621,13 +621,13 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.7.0", "version": "0.7.1",
"resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.7.0.tgz", "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz",
"integrity": "sha512-ejvBr8MQCbVsWNZnCwDXjUKq40MDmHalq7cJ6e9s/qzTUFIIo/afzt1Vui9T97FM/V/pN4YsFVoed5NIa96RDg==", "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@eslint/core": "^1.2.0", "@eslint/core": "^1.2.1",
"levn": "^0.4.1" "levn": "^0.4.1"
}, },
"engines": { "engines": {
@@ -1371,9 +1371,9 @@
} }
}, },
"node_modules/@tanstack/query-core": { "node_modules/@tanstack/query-core": {
"version": "5.99.0", "version": "5.99.2",
"resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.99.0.tgz", "resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.99.2.tgz",
"integrity": "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ==", "integrity": "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",
@@ -1381,13 +1381,13 @@
} }
}, },
"node_modules/@tanstack/vue-query": { "node_modules/@tanstack/vue-query": {
"version": "5.99.0", "version": "5.99.2",
"resolved": "https://registry.npmmirror.com/@tanstack/vue-query/-/vue-query-5.99.0.tgz", "resolved": "https://registry.npmmirror.com/@tanstack/vue-query/-/vue-query-5.99.2.tgz",
"integrity": "sha512-okrHNkouasL5cjo7O8hUxul22DiIL335KUQus/cfoAdCOUytF/OIv3BpNDevfqE6zHqqJ651Fb9OkUJPUCL0ZQ==", "integrity": "sha512-ll7dzgeM/3/ckNsp+J0ZrUuvJEZan3RlOrb1T4M47CJPM0rS0WNanSYQWPgK0A+Eu/ihwm/kEBmAZOA7toHP8g==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/match-sorter-utils": "^8.19.4",
"@tanstack/query-core": "5.99.0", "@tanstack/query-core": "5.99.2",
"@vue/devtools-api": "^6.6.3", "@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.10" "vue-demi": "^0.14.10"
}, },
@@ -1924,9 +1924,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/axios": { "node_modules/axios": {
"version": "1.15.0", "version": "1.15.1",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.15.0.tgz", "resolved": "https://registry.npmmirror.com/axios/-/axios-1.15.1.tgz",
"integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", "integrity": "sha512-WOG+Jj8ZOvR0a3rAn+Tuf1UQJRxw5venr6DgdbJzngJE3qG7X0kL83CZGpdHMxEm+ZK3seAbvFsw4FfOfP9vxg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"follow-redirects": "^1.15.11", "follow-redirects": "^1.15.11",
@@ -2559,18 +2559,18 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "10.2.0", "version": "10.2.1",
"resolved": "https://registry.npmmirror.com/eslint/-/eslint-10.2.0.tgz", "resolved": "https://registry.npmmirror.com/eslint/-/eslint-10.2.1.tgz",
"integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", "integrity": "sha512-wiyGaKsDgqXvF40P8mDwiUp/KQjE1FdrIEJsM8PZ3XCiniTMXS3OHWWUe5FI5agoCnr8x4xPrTDZuxsBlNHl+Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.2", "@eslint-community/regexpp": "^4.12.2",
"@eslint/config-array": "^0.23.4", "@eslint/config-array": "^0.23.5",
"@eslint/config-helpers": "^0.5.4", "@eslint/config-helpers": "^0.5.5",
"@eslint/core": "^1.2.0", "@eslint/core": "^1.2.1",
"@eslint/plugin-kit": "^0.7.0", "@eslint/plugin-kit": "^0.7.1",
"@humanfs/node": "^0.16.6", "@humanfs/node": "^0.16.6",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.4.2", "@humanwhocodes/retry": "^0.4.2",
@@ -5010,9 +5010,9 @@
} }
}, },
"node_modules/typescript": { "node_modules/typescript": {
"version": "6.0.2", "version": "6.0.3",
"resolved": "https://registry.npmmirror.com/typescript/-/typescript-6.0.2.tgz", "resolved": "https://registry.npmmirror.com/typescript/-/typescript-6.0.3.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"devOptional": true, "devOptional": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {

View File

@@ -1,7 +1,7 @@
{ {
"name": "testplant", "name": "testplant",
"private": true, "private": true,
"version": "1.0.1", "version": "0.1.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
@@ -12,11 +12,11 @@
}, },
"dependencies": { "dependencies": {
"@arco-design/color": "^0.4.0", "@arco-design/color": "^0.4.0",
"@arco-design/web-vue": "^2.57.0", "@arco-design/web-vue": "^2.58.0",
"@tanstack/vue-query": "^5.99.0", "@tanstack/vue-query": "^5.99.2",
"@tinymce/tinymce-vue": "^6.3.0", "@tinymce/tinymce-vue": "^6.3.0",
"@vueuse/core": "^14.2.1", "@vueuse/core": "^14.2.1",
"axios": "^1.15.0", "axios": "^1.15.1",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dayjs": "^1.11.20", "dayjs": "^1.11.20",
@@ -50,7 +50,7 @@
"@vitejs/plugin-vue-jsx": "^5.1.5", "@vitejs/plugin-vue-jsx": "^5.1.5",
"@vue/babel-plugin-jsx": "^2.0.1", "@vue/babel-plugin-jsx": "^2.0.1",
"browserslist": "^4.28.2", "browserslist": "^4.28.2",
"eslint": "^10.2.0", "eslint": "^10.2.1",
"eslint-plugin-vue": "^10.8.0", "eslint-plugin-vue": "^10.8.0",
"less": "^4.6.4", "less": "^4.6.4",
"less-loader": "^12.3.2", "less-loader": "^12.3.2",
@@ -58,7 +58,7 @@
"prettier": "^3.8.3", "prettier": "^3.8.3",
"rollup-plugin-visualizer": "^7.0.1", "rollup-plugin-visualizer": "^7.0.1",
"tailwindcss": "^4.2.2", "tailwindcss": "^4.2.2",
"typescript": "^6.0.2", "typescript": "^6.0.3",
"vite": "^8.0.8", "vite": "^8.0.8",
"vue-eslint-parser": "^10.4.0" "vue-eslint-parser": "^10.4.0"
} }

View File

@@ -21,13 +21,7 @@
</template> </template>
<a-spin :loading="dataLoading" tip="加载中..." class="w-full"> <a-spin :loading="dataLoading" tip="加载中..." class="w-full">
<!-- 修改源码parentKey --> <!-- 修改源码parentKey -->
<ma-form <ma-form v-model="form" :columns="formColumns" :parent-key="props.parentKey" :options="formOptions" ref="maFormRef">
v-model="form"
:columns="formColumns"
:parent-key="props.parentKey"
:options="formOptions"
ref="maFormRef"
>
<template v-for="slot in Object.keys($slots)" #[slot]="component"> <template v-for="slot in Object.keys($slots)" #[slot]="component">
<slot :name="slot" v-bind="component" /> <slot :name="slot" v-bind="component" />
</template> </template>
@@ -88,12 +82,17 @@ const submit = async () => {
if (isFunction(options.beforeAdd) && !(await options.beforeAdd(formData))) { if (isFunction(options.beforeAdd) && !(await options.beforeAdd(formData))) {
return false return false
} }
// 修改源码添加parameters参数 try {
if (!options.parameters) { if (!options.parameters) {
response = await options.add.api(formData) response = await options.add.api(formData)
} else { } else {
response = await options.add.api({ ...formData, ...options.parameters }) response = await options.add.api({ ...formData, ...options.parameters })
}
} catch (error) {
console.error("标识重复")
return false
} }
// 修改源码添加parameters参数
isFunction(options.afterAdd) && (await options.afterAdd(response, formData)) isFunction(options.afterAdd) && (await options.afterAdd(response, formData))
} else { } else {
if (isFunction(options.beforeEdit) && !(await options.beforeEdit(formData))) { if (isFunction(options.beforeEdit) && !(await options.beforeEdit(formData))) {

View File

@@ -1,16 +1,8 @@
<!--
- @Author XXX
- @Link XXX
-->
<template> <template>
<component <component
:is="getComponentName()" :is="getComponentName()"
v-model="value" v-model="value"
:placeholder=" :placeholder="props.component.formType === 'range' ? ['请选择开始时间', '请选择结束时间'] : `请选择${props.component.title}`"
props.component.formType === 'range'
? ['请选择开始时间', '请选择结束时间']
: `请选择${props.component.title}`
"
:time-picker-props="props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {}" :time-picker-props="props.component.formType == 'range' ? { defaultValue: ['00:00:00', '23:59:59'] } : {}"
:show-time="props.component.showTime" :show-time="props.component.showTime"
:type="props.component.range ? (props.component.formType === 'time' ? 'time-range' : 'range') : ''" :type="props.component.range ? (props.component.formType === 'time' ? 'time-range' : 'range') : ''"

View File

@@ -51,14 +51,14 @@ const props = defineProps({
// 插件 // 插件
plugins: { plugins: {
type: [String, Array], type: [String, Array],
default: "visualchars code table nonbreaking lists autosave autoresize" default: "visualchars code table nonbreaking autosave autoresize"
// 备份:"searchreplace visualchars code table nonbreaking lists autosave autoresize" // 备份:"searchreplace visualchars code table nonbreaking lists autosave autoresize"
}, },
// 工具栏 // 工具栏
toolbar: { toolbar: {
type: [String, Array], type: [String, Array],
// 如果要取消粘贴只粘贴文本需要用户加格式请加上pastetext // 如果要取消粘贴只粘贴文本需要用户加格式请加上pastetext
default: "code undo redo aligncenter alignleft indent styleselect formatselect fontselect fontsizeselect removeformat" default: "code undo redo aligncenter alignleft indent styleselect formatselect fontselect fontsizeselect"
// 下面是备份配置: // 下面是备份配置:
// default:"code undo redo restoredraft | paste | bold | aligncenter alignleft alignjustify indent searchreplace | \ // default:"code undo redo restoredraft | paste | bold | aligncenter alignleft alignjustify indent searchreplace | \

View File

@@ -1,80 +1,80 @@
<template> <template>
<div class="interface-image-container"> <div class="interface-image-container">
<a-modal <a-modal
v-model:visible="visible" v-model:visible="visible"
width="50%" width="50%"
draggable draggable
:on-before-ok="handleSyncOk" :on-before-ok="handleSyncOk"
unmount-on-close unmount-on-close
ok-text="确认保存" ok-text="确认保存"
cancel-text="关闭不保存" cancel-text="关闭不保存"
:maskClosable="false" :maskClosable="false"
@close="handleOnClose" @close="handleOnClose"
> >
<template #title>软件接口图</template> <template #title>软件接口图</template>
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<ImageInput v-model="imageUrl" v-model:fontnote="fontnote" /> <ImageInput v-model="imageUrl" v-model:fontnote="fontnote" />
</div> </div>
</a-modal> </a-modal>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, getCurrentInstance } from "vue" import { ref, getCurrentInstance } from "vue"
import ImageInput from "./projectModal/ImageInput/index.vue" import ImageInput from "./projectModal/ImageInput/index.vue"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import projectApi from "@/api/project/project" import projectApi from "@/api/project/project"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
const { proxy } = getCurrentInstance() as any const { proxy } = getCurrentInstance() as any
const route = useRoute() const route = useRoute()
const visible = ref(false) const visible = ref(false)
// props // props
const { reset } = defineProps<{ const { reset } = defineProps<{
reset: () => void reset: () => void
}>() }>()
// 题注和图片数据 // 题注和图片数据
const fontnote = ref("") const fontnote = ref("")
const imageUrl = ref("") const imageUrl = ref("")
const handleSyncOk = async () => { const handleSyncOk = async () => {
// 验证题注是否为空 // 验证题注是否为空
if (fontnote.value.trim().length <= 0 || !imageUrl.value.trim()) { if (fontnote.value?.trim().length <= 0 || !imageUrl.value?.trim()) {
Message.error("请输入题注和粘贴图片") Message.error("请输入题注和粘贴图片")
return false return false
} }
// 提交数据 // 提交数据
try { try {
await projectApi.postInterfaceImage(route.query.id, { fontnote: fontnote.value, content: imageUrl.value, type: "image" }) await projectApi.postInterfaceImage(route.query.id, { fontnote: fontnote.value, content: imageUrl.value, type: "image" })
Message.success("保存成功") Message.success("保存成功")
} catch (e) { } catch (e) {
return false return false
} }
} }
const handleOnClose = () => { const handleOnClose = () => {
// 用来清空数据 // 用来清空数据
fontnote.value = "" fontnote.value = ""
imageUrl.value = "" imageUrl.value = ""
reset() reset()
} }
const open = async () => { const open = async () => {
proxy?.$loading?.show("数据加载中...") proxy?.$loading?.show("数据加载中...")
try { try {
const { data } = await projectApi.getInterfaceImage(route.query.id) const { data } = await projectApi.getInterfaceImage(route.query.id)
fontnote.value = data.fontnote fontnote.value = data.fontnote || ""
imageUrl.value = data.content imageUrl.value = data.content || ""
visible.value = true visible.value = true
} catch (e) { } catch (e) {
} finally { } finally {
proxy?.$loading?.hide() proxy?.$loading?.hide()
} }
} }
defineExpose({ open }) defineExpose({ open })
</script> </script>
<style scoped></style> <style scoped></style>

View File

@@ -1,95 +1,95 @@
<template> <template>
<div class="static-dynamic-table-container"> <div class="static-dynamic-table-container">
<a-modal <a-modal
v-model:visible="visible" v-model:visible="visible"
width="50%" width="50%"
draggable draggable
:on-before-ok="handleSyncOk" :on-before-ok="handleSyncOk"
unmount-on-close unmount-on-close
ok-text="确认保存" ok-text="确认保存"
cancel-text="关闭不保存" cancel-text="关闭不保存"
:maskClosable="false" :maskClosable="false"
@close="handleOnClose" @close="handleOnClose"
> >
<template #title>{{ theTitle }}</template> <template #title>{{ theTitle }}</template>
<WordLikeTable v-model="tableData" v-model:fontnote="fontnote" /> <WordLikeTable v-model="tableData" v-model:fontnote="fontnote" />
</a-modal> </a-modal>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import { getCurrentInstance, ref } from "vue" import { getCurrentInstance, ref } from "vue"
import WordLikeTable from "./projectModal/wordLikeTable/index.vue" import WordLikeTable from "./projectModal/wordLikeTable/index.vue"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import { cloneDeep } from "lodash-es" import { cloneDeep } from "lodash-es"
import projectApi from "@/api/project/project" import projectApi from "@/api/project/project"
const { proxy } = getCurrentInstance() as any const { proxy } = getCurrentInstance() as any
const route = useRoute() const route = useRoute()
// props // props
const { reset } = defineProps<{ const { reset } = defineProps<{
reset: () => void reset: () => void
}>() }>()
const visible = ref(false) const visible = ref(false)
const theTitle = ref("") const theTitle = ref("")
const tableInitValue = [ const tableInitValue = [
["", "", ""], ["", "", ""],
["", "", ""], ["", "", ""],
["", "", ""] ["", "", ""]
] ]
const tableData = ref(tableInitValue) const tableData = ref(tableInitValue)
const fontnote = ref("") const fontnote = ref("")
const handleSyncOk = async () => { const handleSyncOk = async () => {
// 验证题注是否为空 // 验证题注是否为空
if (tableData.value.length <= 0) { if (tableData.value?.length <= 0) {
Message.error("请输入表格内容再提交") Message.error("请输入表格内容再提交")
return false return false
} }
try { try {
// 请求接口 // 请求接口
await projectApi.postStaticDynamicItems({ await projectApi.postStaticDynamicItems({
id: route.query.id, id: route.query.id,
category: theTitle.value, category: theTitle.value,
table: tableData.value, table: tableData.value,
fontnote: fontnote.value fontnote: fontnote.value
}) })
Message.success("保存成功") Message.success("保存成功")
} catch (e) { } catch (e) {
return false return false
} }
} }
const handleOnClose = () => { const handleOnClose = () => {
// 用来清空数据 // 用来清空数据
tableData.value = cloneDeep(tableInitValue) tableData.value = cloneDeep(tableInitValue)
fontnote.value = "" fontnote.value = ""
reset() reset()
} }
const open = async (title: string) => { const open = async (title: string) => {
proxy?.$loading?.show("数据加载中...") proxy?.$loading?.show("数据加载中...")
theTitle.value = title theTitle.value = title
try { try {
// 获取数据并赋值给tableData // 获取数据并赋值给tableData
const res = await projectApi.getStaticDynamicItems(route.query.id, title) const res = await projectApi.getStaticDynamicItems(route.query.id, title)
if (res.code === 25001) { if (res.code === 25001) {
const data = res.data const data = res.data
tableData.value = data.table tableData.value = data.table
fontnote.value = data.fontnote fontnote.value = data.fontnote || ""
} }
visible.value = true visible.value = true
} catch (e) { } catch (e) {
} finally { } finally {
proxy?.$loading?.hide() proxy?.$loading?.hide()
} }
} }
defineExpose({ open }) defineExpose({ open })
</script> </script>
<style scoped></style> <style scoped></style>

View File

@@ -1,107 +1,107 @@
<template> <template>
<div class="text-table-container"> <div class="text-table-container">
<a-modal <a-modal
v-model:visible="visible" v-model:visible="visible"
width="50%" width="50%"
draggable draggable
:on-before-ok="handleSyncOk" :on-before-ok="handleSyncOk"
unmount-on-close unmount-on-close
ok-text="确认保存" ok-text="确认保存"
cancel-text="关闭不保存" cancel-text="关闭不保存"
:maskClosable="false" :maskClosable="false"
@close="handleOnClose" @close="handleOnClose"
> >
<template #title>{{ theTitle }}</template> <template #title>{{ theTitle }}</template>
<a-space direction="vertical" fill> <a-space direction="vertical" fill>
<a-card title="差异性段落描述" hoverable> <a-card title="差异性段落描述" hoverable>
<a-textarea auto-size placeholder="请填写差异性分析和'见下表所示'" v-model="description"></a-textarea> <a-textarea auto-size placeholder="请填写差异性分析和'见下表所示'" v-model="description"></a-textarea>
</a-card> </a-card>
<a-card title="表格" hoverable> <a-card title="表格" hoverable>
<WordLikeTable v-model="tableDatas" v-model:fontnote="fontnote" /> <WordLikeTable v-model="tableDatas" v-model:fontnote="fontnote" />
</a-card> </a-card>
</a-space> </a-space>
</a-modal> </a-modal>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import { getCurrentInstance, ref } from "vue" import { getCurrentInstance, ref } from "vue"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import WordLikeTable from "./projectModal/wordLikeTable/index.vue" import WordLikeTable from "./projectModal/wordLikeTable/index.vue"
import { cloneDeep } from "lodash-es" import { cloneDeep } from "lodash-es"
import projectApi from "@/api/project/project" import projectApi from "@/api/project/project"
const { proxy } = getCurrentInstance() as any const { proxy } = getCurrentInstance() as any
const route = useRoute() const route = useRoute()
// datas // datas
const description = ref("") const description = ref("")
const initialTableData = [ const initialTableData = [
["", "", ""], ["", "", ""],
["", "", ""], ["", "", ""],
["", "", ""] ["", "", ""]
] ]
const fontnote = ref("") const fontnote = ref("")
const tableDatas = ref(initialTableData) const tableDatas = ref(initialTableData)
// props // props
const { reset } = defineProps<{ const { reset } = defineProps<{
reset: () => void reset: () => void
}>() }>()
const visible = ref(false) const visible = ref(false)
const theTitle = ref("") const theTitle = ref("")
const handleSyncOk = async () => { const handleSyncOk = async () => {
// 验证输入文字是否为空 // 验证输入文字是否为空
if (description.value.trim().length <= 0) { if (description.value.trim().length <= 0) {
Message.error("请输入分析内容再提交") Message.error("请输入分析内容再提交")
return false return false
} }
try { try {
// 请求接口 // 请求接口
await projectApi.postEnvAnalysis({ await projectApi.postEnvAnalysis({
id: route.query.id, id: route.query.id,
table: tableDatas.value, table: tableDatas.value,
fontnote: fontnote.value, fontnote: fontnote.value,
description: description.value description: description.value
}) })
Message.success("保存成功") Message.success("保存成功")
} catch (e) { } catch (e) {
return false return false
} }
} }
const handleOnClose = () => { const handleOnClose = () => {
// 用来清空数据 // 用来清空数据
fontnote.value = "" fontnote.value = ""
description.value = "" description.value = ""
tableDatas.value = cloneDeep(initialTableData) tableDatas.value = cloneDeep(initialTableData)
reset() reset()
} }
const open = async (category_str: string) => { const open = async (category_str: string) => {
proxy?.$loading?.show("数据加载中...") proxy?.$loading?.show("数据加载中...")
theTitle.value = category_str theTitle.value = category_str
try { try {
// 获取数据并赋值给tableData // 获取数据并赋值给tableData
const res = await projectApi.getEnvAnalysis(route.query.id) const res = await projectApi.getEnvAnalysis(route.query.id)
if (res.code === 25001) { if (res.code === 25001) {
tableDatas.value = res.data.table tableDatas.value = res.data.table
fontnote.value = res.data.fontnote fontnote.value = res.data.fontnote || ""
description.value = res.data.description description.value = res.data.description || ""
} }
visible.value = true visible.value = true
} catch (e) { } catch (e) {
} finally { } finally {
proxy?.$loading?.hide() proxy?.$loading?.hide()
} }
} }
defineExpose({ defineExpose({
open open
}) })
</script> </script>
<style scoped></style> <style scoped></style>

View File

@@ -1,95 +1,114 @@
<template> <template>
<!-- 组件该组件显示一个正方体可粘贴图片内容进去并展示并生成base64到数据里面 --> <!-- 组件该组件显示一个正方体可粘贴图片内容进去并展示并生成base64到数据里面 -->
<div class="image-input-container flex flex-col gap-1"> <div class="image-input-container flex flex-col gap-1">
<div class="image-input-handle flex items-center justify-center" @paste="handlePaste"> <div class="image-input-handle flex items-center justify-center" @paste="handlePaste">
<!-- 加载状态 --> <!-- 加载状态 -->
<div v-if="isLoading"> <div v-if="isLoading">
<a-spin :size="32" /> <a-spin :size="32" />
</div> </div>
<!-- 正常显示状态 --> <!-- 正常显示状态 -->
<template v-else-if="imgData"> <template v-else-if="imgData">
<img :src="imgData" alt="粘贴的图片" class="preview-image img-container" /> <img :src="imgData" alt="粘贴的图片" class="preview-image img-container" />
</template> </template>
<template v-else> <template v-else>
<div class="placeholder">此处Ctrl+V粘贴图片</div> <div class="placeholder">此处Ctrl+V粘贴图片</div>
</template> </template>
</div> </div>
<!-- 题注fontnote --> <!-- 题注fontnote -->
<a-input v-model="fontnote" class="max-w-100" placeholder="请输入题注"></a-input> <a-input v-model="fontnote" class="max-w-100" placeholder="请输入题注"></a-input>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import { nextTick, ref } from "vue" import { nextTick, ref, onMounted, onBeforeUnmount } from "vue"
// 储存图片base64 const imgData = defineModel<string>()
const imgData = defineModel<string>() const fontnote = defineModel<string>("fontnote")
// 储存题注 const isLoading = ref(false)
const fontnote = defineModel<string>("fontnote")
// 加载状态 // 全局粘贴处理函数
const isLoading = ref(false) const onGlobalPaste = (e: ClipboardEvent) => {
// 可以增加判断:只有当前组件所在的 modal 打开时才处理(通过 props 传入 visible 状态或使用 provide/inject
// 处理粘贴事件 // 简单起见,这里假设始终需要处理(父组件控制显示隐藏)
const handlePaste = async (e: ClipboardEvent) => { handlePaste(e)
e.preventDefault() }
// 处理没有粘贴内容
if (!e.clipboardData) return // 处理粘贴逻辑(复用之前的修复版)
const items = e.clipboardData!.items const handlePaste = (e: ClipboardEvent) => {
// 遍历粘贴板内容 e.preventDefault()
for (let i = 0; i < items.length; i++) { const clipboardData = e.clipboardData
const item = items[i] if (!clipboardData) return
// 判断是否是粘贴的图片
if (item.kind === "file" && item.type.startsWith("image/")) { const items = clipboardData.items
const file = item.getAsFile() let imageItem: DataTransferItem | null = null
if (!file) {
Message.error("读取图片失败,请重新粘贴") for (let i = 0; i < items.length; i++) {
break const item = items[i]
} if (item.kind === "file" && item.type.startsWith("image/")) {
// 判断大小不超过50M imageItem = item
if (file.size > 50 * 1024 * 1024) { break
Message.error("要求图片不超过50M") }
break }
}
isLoading.value = true if (!imageItem) {
const reader = new FileReader() Message.error("请粘贴图片,无法粘贴文字或其他内容")
reader.onload = async (e: ProgressEvent<FileReader>) => { return
imgData.value = e.target!.result as string }
await nextTick() // 保证图片展示
isLoading.value = false const file = imageItem.getAsFile()
} if (!file) {
// 加载失败处理 Message.error("读取图片失败,请重新粘贴")
reader.onerror = () => { return
Message.error("图片加载失败,请重试") }
isLoading.value = false
} if (file.size > 50 * 1024 * 1024) {
reader.readAsDataURL(file) // 可直接转为base64的url给img元素使用 Message.error("要求图片不超过50M")
break return
} else { }
Message.error("请粘贴图片,无法粘贴文字或其他内容")
break isLoading.value = true
} const reader = new FileReader()
} reader.onload = (e) => {
} imgData.value = e.target!.result as string
</script> nextTick(() => {
isLoading.value = false
<style scoped lang="less"> })
.image-input-handle { }
width: 200px; reader.onerror = () => {
height: 180px; Message.error("图片加载失败,请重试")
border: 1px solid #eee; isLoading.value = false
cursor: alias; }
reader.readAsDataURL(file)
.img-container{ }
width: 100%;
height: 100%; // 挂载全局监听
} onMounted(() => {
} document.addEventListener("paste", onGlobalPaste)
})
.preview-image {
max-width: 100%; onBeforeUnmount(() => {
max-height: 100%; document.removeEventListener("paste", onGlobalPaste)
object-fit: contain; })
} </script>
</style>
<style scoped lang="less">
.image-input-handle {
width: 200px;
height: 180px;
border: 1px solid #eee;
cursor: alias;
.img-container {
width: 100%;
height: 100%;
}
}
.preview-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
</style>

View File

@@ -1,122 +1,123 @@
<template> <template>
<!-- 完成自定义表格 --> <!-- 完成自定义表格 -->
<div class="fontnote"> <div class="fontnote">
<a-space class="w-full"> <a-space class="w-full">
<span>题注</span> <span>题注</span>
<a-input v-model="fontnote" :style="{ width: '500px' }"></a-input> <a-input v-model="fontnote" :style="{ width: '500px' }"></a-input>
</a-space> </a-space>
</div> <a-alert type="warning" class="mt-2">表格第一行为[表头]测评数据/硬件项表格会自动添加序号列而其他纯自定义表格动态环境描述软件概述不会添加序号列</a-alert>
<div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover"> </div>
<div class="arco-table-container"> <div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
<table class="arco-table-element" cellpadding="0" cellspacing="0"> <div class="arco-table-container">
<thead> <table class="arco-table-element" cellpadding="0" cellspacing="0">
<tr class="arco-table-tr"> <thead>
<th class="arco-table-th" v-for="(_, colIndex) in datas![0]" :key="colIndex"> <tr class="arco-table-tr">
<span class="arco-table-cell items-center justify-center"> <th class="arco-table-th" v-for="(_, colIndex) in datas![0]" :key="colIndex">
<a-tooltip content="此列后新增列"> <span class="arco-table-cell items-center justify-center">
<a-button type="text" size="mini" @click="addColumn(colIndex)" class="delete-col-btn"> <a-tooltip content="此列后新增列">
<template #icon> <a-button type="text" size="mini" @click="addColumn(colIndex)" class="delete-col-btn">
<icon-plus /> <template #icon>
</template> <icon-plus />
</a-button> </template>
</a-tooltip> </a-button>
<a-tooltip content="删除该列"> </a-tooltip>
<a-button <a-tooltip content="删除该列">
type="text" <a-button
size="mini" type="text"
status="danger" size="mini"
@click="deleteColumn(colIndex)" status="danger"
:disabled="datas![0].length <= 1" @click="deleteColumn(colIndex)"
class="delete-col-btn" :disabled="datas![0].length <= 1"
> class="delete-col-btn"
<template #icon> >
<icon-close /> <template #icon>
</template> <icon-close />
</a-button> </template>
</a-tooltip> </a-button>
</span> </a-tooltip>
</th> </span>
<th class="arco-table-th" :style="{ textAlign: 'center' }"> </th>
<span>操作</span> <th class="arco-table-th" :style="{ textAlign: 'center' }">
</th> <span>操作</span>
</tr> </th>
</thead> </tr>
<tbody> </thead>
<tr class="arco-table-tr" v-for="(row, rowIndex) in datas" :key="rowIndex"> <tbody>
<td class="arco-table-td" v-for="(col, colIndex) in row" :key="colIndex"> <tr class="arco-table-tr" v-for="(row, rowIndex) in datas" :key="rowIndex">
<span class="arco-table-cell"> <td class="arco-table-td" v-for="(col, colIndex) in row" :key="colIndex">
<a-textarea auto-size v-model="datas![rowIndex][colIndex]" /> <span class="arco-table-cell">
</span> <a-textarea auto-size v-model="datas![rowIndex][colIndex]" />
</td> </span>
<td class="arco-table-td"> </td>
<span class="arco-table-cell items-center justify-center"> <td class="arco-table-td">
<a-tooltip content="此行后新增行"> <span class="arco-table-cell items-center justify-center">
<a-button type="text" size="mini" @click="addRow(rowIndex)" class="delete-col-btn"> <a-tooltip content="此行后新增行">
<template #icon> <a-button type="text" size="mini" @click="addRow(rowIndex)" class="delete-col-btn">
<icon-plus /> <template #icon>
</template> <icon-plus />
</a-button> </template>
</a-tooltip> </a-button>
<a-tooltip content="删除该行"> </a-tooltip>
<a-button size="mini" type="text" status="danger" @click="deleteRow(rowIndex)" :disabled="datas!.length <= 1"> <a-tooltip content="删除该行">
<template #icon> <a-button size="mini" type="text" status="danger" @click="deleteRow(rowIndex)" :disabled="datas!.length <= 1">
<icon-delete /> <template #icon>
</template> <icon-delete />
</a-button> </template>
</a-tooltip> </a-button>
</span> </a-tooltip>
</td> </span>
</tr> </td>
</tbody> </tr>
</table> </tbody>
</div> </table>
</div> </div>
</template> </div>
</template>
<script setup lang="ts">
// 该组件储存数据 <script setup lang="ts">
const fontnote = defineModel<string>("fontnote") // 该组件储存数据
const fontnote = defineModel<string>("fontnote")
const datas = defineModel<string[][]>()
const datas = defineModel<string[][]>()
// 行列操作
const deleteRow = (rowIndex: number) => { // 行列操作
datas.value!.splice(rowIndex, 1) const deleteRow = (rowIndex: number) => {
} datas.value!.splice(rowIndex, 1)
const deleteColumn = (colIndex: number) => { }
datas.value!.forEach((row) => { const deleteColumn = (colIndex: number) => {
row.splice(colIndex, 1) datas.value!.forEach((row) => {
}) row.splice(colIndex, 1)
} })
const addRow = (rowIndex: number) => { }
const newRow = new Array(datas.value![0].length).fill("") const addRow = (rowIndex: number) => {
datas.value!.splice(rowIndex + 1, 0, newRow) const newRow = new Array(datas.value![0].length).fill("")
} datas.value!.splice(rowIndex + 1, 0, newRow)
const addColumn = (colIndex: number) => { }
// 处理空表格的特殊情况 const addColumn = (colIndex: number) => {
if (datas.value!.length === 0) { // 处理空表格的特殊情况
datas.value!.push([""]) if (datas.value!.length === 0) {
return datas.value!.push([""])
} return
// 新增后续列 }
datas.value!.forEach((row) => { // 新增后续列
const insertPosition = colIndex === -1 ? row.length : colIndex + 1 datas.value!.forEach((row) => {
row.splice(insertPosition, 0, "") const insertPosition = colIndex === -1 ? row.length : colIndex + 1
}) row.splice(insertPosition, 0, "")
} })
</script> }
</script>
<style scoped lang="less">
.fontnote { <style scoped lang="less">
margin: 10px 0; .fontnote {
width: 100%; margin: 10px 0;
} width: 100%;
}
.arco-textarea {
min-width: 120px; .arco-textarea {
} min-width: 120px;
}
.arco-table-cell {
padding: 5px; .arco-table-cell {
} padding: 5px;
</style> }
</style>

View File

@@ -70,7 +70,8 @@ const handleRelatedChange = async (record) => {
// 因为switch绑定了record.related所以可以动态改变 // 因为switch绑定了record.related所以可以动态改变
loading.value = true loading.value = true
// 判断该用例是否是未通过,如果未执行或已通过则不允许关联问题单 // 判断该用例是否是未通过,如果未执行或已通过则不允许关联问题单
if (!caseIsPassed(caseInfo.value)) { // 修改当用户取消关联时应该不判断是否caseIsPassed
if (record.related && !caseIsPassed(caseInfo.value)) {
Message.error("该用例没有缓存或无未通过步骤,请切换页面或设置未通过步骤后添加问题单!") Message.error("该用例没有缓存或无未通过步骤,请切换页面或设置未通过步骤后添加问题单!")
loading.value = false loading.value = false
record.related = !record.related record.related = !record.related
@@ -90,7 +91,7 @@ const handleRelatedChange = async (record) => {
}) })
if (res) { if (res) {
if (!res.data.isOK) { if (!res.data.isOK) {
// 后台说没关联成功则保持不变 // 后端返回失败则回退switch状态
record.related = !record.related record.related = !record.related
loading.value = false loading.value = false
} }

View File

@@ -1,349 +1,348 @@
<template> <template>
<div class="ai-modal-container"> <div class="ai-modal-container">
<a-modal v-model:visible="visible" width="90%" unmount-on-close draggable :footer="false"> <a-modal v-model:visible="visible" width="90%" unmount-on-close draggable :footer="false">
<template #title> AI生成测试项 </template> <template #title> AI生成测试项 </template>
<div class="flex flex-col"> <div class="flex flex-col">
<a-button type="primary" :disabled="generateLoading" @click="generateClick">{{ <a-button type="primary" :disabled="generateLoading" @click="generateClick">{{
generateLoading ? "AI正在生成测试项中..." : "点击生成测试项" generateLoading ? "AI正在生成测试项中..." : "点击生成测试项"
}}</a-button> }}</a-button>
<a-progress <a-progress
:percent="percent" :percent="percent"
:style="{ width: '100%' }" :style="{ width: '100%' }"
size="large" size="large"
:show-text="false" :show-text="false"
:color="{ :color="{
'0%': 'rgb(var(--primary-6))', '0%': 'rgb(var(--primary-6))',
'100%': 'rgb(var(--success-6))' '100%': 'rgb(var(--success-6))'
}" }"
class="mb-2" class="mb-2"
/> />
<a-list :loading="listLoading" :data="dataList"> <a-list :loading="listLoading" :data="dataList">
<template #header> 设计需求{{ designObj?.name ?? "暂无内容" }} </template> <template #header> 设计需求{{ designObj?.name ?? "暂无内容" }} </template>
<template #item="{ item, index }"> <template #item="{ item, index }">
<a-list-item> <a-list-item>
<div class="item-container"> <div class="item-container">
<a-input-group> <a-input-group>
<div class="index-hao">{{ indexTu[index] }}</div> <div class="index-hao">{{ indexTu[index] }}</div>
<span class="label">测试项</span> <span class="label">测试项</span>
<a-input placeholder="测试项标识" v-model="item.ident" :style="{ width: '100px' }" @click.stop.prevent></a-input> <a-input placeholder="测试项标识" v-model="item.ident" :style="{ width: '100px' }" @click.stop.prevent></a-input>
<a-input placeholder="测试项名称" v-model="item.title" :style="{ width: '250px' }" @click.stop.prevent></a-input> <a-input placeholder="测试项名称" v-model="item.title" :style="{ width: '250px' }" @click.stop.prevent></a-input>
<a-select placeholder="选择优先级" v-model="item.priority" :style="{ width: '150px' }"> <a-select placeholder="选择优先级" v-model="item.priority" :style="{ width: '150px' }">
<a-option value="1"></a-option> <a-option value="1"></a-option>
<a-option value="2"></a-option> <a-option value="2"></a-option>
<a-option value="3"></a-option> <a-option value="3"></a-option>
</a-select> </a-select>
<a-select placeholder="选择测试类型" v-model="item.testType" :style="{ width: '200px' }"> <a-select placeholder="选择测试类型" v-model="item.testType" :style="{ width: '200px' }">
<a-option v-for="type in testType" :key="type.key" :value="type.key"> <a-option v-for="type in testType" :key="type.key" :value="type.key">
{{ type.title }} {{ type.title }}
</a-option> </a-option>
</a-select> </a-select>
<a-select placeholder="选择测试手段" multiple v-model="item.testMethod" :style="{ width: '400px' }"> <a-select placeholder="选择测试手段" multiple v-model="item.testMethod" :style="{ width: '400px' }">
<a-option v-for="method in testMethod" :key="method.key" :value="method.key"> <a-option v-for="method in testMethod" :key="method.key" :value="method.key">
{{ method.title }} {{ method.title }}
</a-option> </a-option>
</a-select> </a-select>
</a-input-group> </a-input-group>
<div class="m-2 flex justify-start items-center"> <div class="m-2 flex justify-start items-center">
<template v-if="isFPGA"> <template v-if="isFPGA">
<div class="label">测试项描述</div> <div class="label">测试项描述</div>
<div class="input flex-1"> <div class="input flex-1">
<a-input v-model="item.demandDescription"></a-input> <a-input v-model="item.demandDescription"></a-input>
</div> </div>
</template> </template>
</div> </div>
<div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover"> <div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
<div class="arco-table-container"> <div class="arco-table-container">
<table class="arco-table-element" cellpadding="0" cellspacing="0"> <table class="arco-table-element" cellpadding="0" cellspacing="0">
<thead> <thead>
<tr class="arco-table-tr"> <tr class="arco-table-tr">
<th class="arco-table-th" :width="100"> <th class="arco-table-th" :width="100">
<span class="arco-table-cell arco-table-cell-align-center"> <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> </span>
</th> </th>
<th class="arco-table-th" :width="200"> <th class="arco-table-th" :width="200">
<span class="arco-table-cell arco-table-cell-align-center"> <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> </span>
</th> </th>
<template v-if="!isFPGA"> <template v-if="!isFPGA">
<th class="arco-table-th" :width="250"> <th class="arco-table-th" :width="250">
<span class="arco-table-cell arco-table-cell-align-center"> <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> </span>
</th> </th>
</template> </template>
<th class="arco-table-th" :width="800"> <th class="arco-table-th" :width="800">
<span class="arco-table-cell arco-table-cell-align-center"> <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> </span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- 这里tr要v-for渲染 --> <!-- 这里tr要v-for渲染 -->
<tr class="arco-table-tr" v-for="(row, idx) in item.children" :key="idx"> <tr class="arco-table-tr" v-for="(row, idx) in item.children" :key="idx">
<td class="arco-table-td"> <td class="arco-table-td">
<span class="arco-table-cell arco-table-cell-align-center"> <span class="arco-table-cell arco-table-cell-align-center">
{{ idx + 1 }} {{ Number(idx) + 1 }}
</span> </span>
</td> </td>
<td class="arco-table-td"> <td class="arco-table-td">
<span class="arco-table-cell"> <span class="arco-table-cell">
<a-textarea auto-size placeholder="请填写测试子项名称" v-model="row.name"></a-textarea> <a-textarea auto-size placeholder="请填写测试子项名称" v-model="row.name"></a-textarea>
</span> </span>
</td> </td>
<template v-if="!isFPGA"> <template v-if="!isFPGA">
<td class="arco-table-td"> <td class="arco-table-td">
<span class="arco-table-cell"> <span class="arco-table-cell">
<a-textarea <a-textarea
auto-size auto-size
placeholder="请填写测试子项描述" placeholder="请填写测试子项描述"
v-model="row.subDescription" v-model="row.subDescription"
></a-textarea> ></a-textarea>
</span> </span>
</td> </td>
</template> </template>
<td class="arco-table-td"> <td class="arco-table-td">
<span class="arco-table-cell"> <span class="arco-table-cell">
<OpeAndExpect v-model="row.subStep" /> <OpeAndExpect v-model="row.subStep" />
</span> </span>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>
</div> </div>
</a-list-item> </a-list-item>
</template> </template>
</a-list> </a-list>
<div class="luButton"> <div class="luButton">
<a-button :loading="luButtonLoading" type="primary" @click="luButtonClick">确认录入测试项</a-button> <a-button :loading="luButtonLoading" type="primary" @click="luButtonClick">确认录入测试项</a-button>
</div> </div>
</div> </div>
<ParentPreview :parent-key="currentKey" /> <ParentPreview :parent-key="currentKey" />
</a-modal> </a-modal>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onUnmounted, ref } from "vue" import { onUnmounted, ref } from "vue"
import { useRoute } from "vue-router" import { useRoute } from "vue-router"
import designApi from "@/api/project/designDemand" import designApi from "@/api/project/designDemand"
import dictApi from "@/api/common" import dictApi from "@/api/common"
import OpeAndExpect from "./OpeAndExpect.vue" // 操作和预期子表格 import OpeAndExpect from "./OpeAndExpect.vue" // 操作和预期子表格
import aiApi from "@/api/outs/aiApi" import aiApi from "@/api/outs/aiApi"
import { Message } from "@arco-design/web-vue" import { Message } from "@arco-design/web-vue"
import tool from "@/utils/tool" import tool from "@/utils/tool"
import { isEmpty } from "lodash-es" import { isEmpty } from "lodash-es"
import demandApi from "@/api/project/testDemand" import demandApi from "@/api/project/testDemand"
import ParentPreview from "@/views/project/ParentPreview/index.vue" import ParentPreview from "@/views/project/ParentPreview/index.vue"
// 常量 // 常量
const route = useRoute() const route = useRoute()
const indexTu = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚" const indexTu = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚"
const isFPGA = tool.checkForCpuOrFPGA(route.query.plant_type) const isFPGA = tool.checkForCpuOrFPGA(route.query.plant_type)
// 初始化测试类型-一起请求处理错误 // 初始化测试类型-一起请求处理错误
const testType = ref<any>([]) const testType = ref<any>([])
const testMethod = ref<any>([]) const testMethod = ref<any>([])
const fetchTestType = async () => { const fetchTestType = async () => {
try { try {
const [typeResponse, methodResponse] = await Promise.all([dictApi.getDict("testType"), dictApi.getDict("testMethod")]) const [typeResponse, methodResponse] = await Promise.all([dictApi.getDict("testType"), dictApi.getDict("testMethod")])
testType.value = typeResponse.data testType.value = typeResponse.data
testMethod.value = methodResponse.data testMethod.value = methodResponse.data
} catch (e) { } catch (e) {
Message.error("初始化测试类型或测试手段错误,请检查网络后重试!") Message.error("初始化测试类型或测试手段错误,请检查网络后重试!")
} }
} }
fetchTestType() fetchTestType()
// 初始化设计需求 // 初始化设计需求
const currentKey: string = route.query.key as string
const currentKey: string = route.query.key as string const getDesign = async () => {
const getDesign = async () => { try {
try { const res = await designApi.getDesignDemandOne({ project_id: route.query.id, key: route.query.key })
const res = await designApi.getDesignDemandOne({ project_id: route.query.id, key: route.query.key }) designObj.value = res.data
designObj.value = res.data } catch (e) {
} catch (e) { Message.error("初始化设计需求信息错误,请检查网络后重试!")
Message.error("初始化设计需求信息错误,请检查网络后重试!") }
} }
} getDesign()
getDesign() const designObj: any = ref()
const designObj: any = ref()
// 进度条和列表加载loading
// 进度条和列表加载loading const percent = ref(0.0)
const percent = ref(0.0) const listLoading = ref(false)
const listLoading = ref(false)
// 根据测试项生成按钮
// 根据测试项生成按钮 const generateLoading = ref(false)
const generateLoading = ref(false) const generateClick = async () => {
const generateClick = async () => { try {
try { generateLoading.value = true
generateLoading.value = true listLoading.value = true
listLoading.value = true percent.value = 0.1 // 开始进度
percent.value = 0.1 // 开始进度 startProgressSimulation()
startProgressSimulation() // 变量给AI的问题
// 变量给AI的问题 const question = tool.htmlToTextWithDOM(designObj.value?.description || "")
const question = tool.htmlToTextWithDOM(designObj.value?.description || "") // 请求后处理结果
// 请求后处理结果 const res = await aiApi.getAiTestItem({ question: question, stream: false })
const res = await aiApi.getAiTestItem({ question: question, stream: false }) // 判断真实接口和开发环境接口
// 判断真实接口和开发环境接口 let tempSolve: any = null
let tempSolve: any = null if (res.data) {
if (res.data) { // 说明是开发环境
// 说明是开发环境 tempSolve = res.data
tempSolve = res.data } else {
} else { tempSolve = res
tempSolve = res }
} const solveRes = JSON.parse(tempSolve.history[0].at(-1))
const solveRes = JSON.parse(tempSolve.history[0].at(-1)) console.log("AI生成测试项结果", solveRes)
console.log("AI生成测试项结果", solveRes) // 给Vue渲染测试项
// 给Vue渲染测试项 dataList.value = solveRes
dataList.value = solveRes dataList.value.forEach((it: any) => {
dataList.value.forEach((it: any) => { it.ident = designObj.value.ident
it.ident = designObj.value.ident it.priority = "1"
it.priority = "1" it.testType = "4"
it.testType = "4" it.testMethod = ["4"]
it.testMethod = ["4"] })
}) percent.value = 1.0 // 完成进度
percent.value = 1.0 // 完成进度 Message.success("生成测试项成功,请完善信息后录入数据")
Message.success("生成测试项成功,请完善信息后录入数据") } catch (e) {
} catch (e) { console.log(e)
console.log(e) percent.value = 0.0
percent.value = 0.0 } finally {
} finally { stopProgressSimulation()
stopProgressSimulation() generateLoading.value = false
generateLoading.value = false setTimeout(() => {
setTimeout(() => { listLoading.value = false
listLoading.value = false }, 500)
}, 500) }
} }
}
// 生成的AI测试项数据
// 生成的AI测试项数据 const dataList = ref([])
const dataList = ref([])
// 进度条模拟变量和函数
// 进度条模拟变量和函数 const progressInterval = ref<NodeJS.Timeout>()
const progressInterval = ref<NodeJS.Timeout>() const startProgressSimulation = () => {
const startProgressSimulation = () => { progressInterval.value = setInterval(() => {
progressInterval.value = setInterval(() => { if (percent.value < 0.8) {
if (percent.value < 0.8) { percent.value += (0.8 - percent.value) * 0.1
percent.value += (0.8 - percent.value) * 0.1 }
} }, 200)
}, 200) }
}
const stopProgressSimulation = () => {
const stopProgressSimulation = () => { if (progressInterval.value) {
if (progressInterval.value) { clearInterval(progressInterval.value)
clearInterval(progressInterval.value) progressInterval.value = undefined
progressInterval.value = undefined }
} }
}
onUnmounted(() => {
onUnmounted(() => { stopProgressSimulation()
stopProgressSimulation() })
})
// defineModel
// defineModel const visible = defineModel<boolean>("visible", { default: false })
const visible = defineModel<boolean>("visible", { default: false })
// 录入按钮相关
// 录入按钮相关 const luButtonLoading = ref(false)
const luButtonLoading = ref(false) const emit = defineEmits(["updateTable"])
const emit = defineEmits(["updateTable"]) const luButtonClick = async () => {
const luButtonClick = async () => { // 1.检查是否还未生成测试项
// 1.检查是否还未生成测试项 if (isEmpty(dataList.value)) {
if (isEmpty(dataList.value)) { Message.warning("您还未生成测试项,请生成后再试")
Message.warning("您还未生成测试项,请生成后再试") return
return }
} // 2.检查测试项标识、测试项名称、优先级、测试类型、测试手段是否填写
// 2.检查测试项标识、测试项名称、优先级、测试类型、测试手段是否填写 const testItem: any = dataList.value.at(0)
const testItem: any = dataList.value.at(0) if (!testItem.title.trim()) {
if (!testItem.title.trim()) { Message.warning("请先填写测试项名称!")
Message.warning("请先填写测试项名称!") return
return }
} if (!testItem.demandDescription.trim()) {
if (!testItem.demandDescription.trim()) { Message.warning("请填写测试项描述后再试!")
Message.warning("请填写测试项描述后再试!") return
return }
} if (testItem.testMethod.length === 0) {
if (testItem.testMethod.length === 0) { Message.warning("请先选择测试手段后再试!")
Message.warning("请先选择测试手段后再试!") return
return }
} // 3.组装接口需要的数据
// 3.组装接口需要的数据 const projectId = route.query.id
const projectId = route.query.id const splitKey: string[] = (route.query.key as any).split("-")
const splitKey: string[] = (route.query.key as any).split("-") const round = splitKey[0]
const round = splitKey[0] const dut = splitKey[1]
const dut = splitKey[1] const designDemand = splitKey.at(-1)
const designDemand = splitKey.at(-1) const adequacy: string = "测试用例覆盖测试子项要求的全部内容。\n所有用例执行完毕对于未执行用例说明未执行原因。"
const adequacy: string = "测试用例覆盖测试子项要求的全部内容。\n所有用例执行完毕对于未执行用例说明未执行原因。" const ident = testItem.ident
const ident = testItem.ident const name = testItem.title
const name = testItem.title const testType = testItem.testType
const testType = testItem.testType const testMethod = testItem.testMethod
const testMethod = testItem.testMethod const testDesciption = testItem.demandDescription
const testDesciption = testItem.demandDescription const priority = testItem.priority
const priority = testItem.priority const testContent = testItem.children.map(({ name: subName, ...rest }) => ({
const testContent = testItem.children.map(({ name: subName, ...rest }) => ({ subName,
subName, ...rest
...rest }))
})) // 4.异步录入啦
// 4.异步录入啦 try {
try { // 首先设置状态
// 首先设置状态 luButtonLoading.value = true
luButtonLoading.value = true generateLoading.value = true
generateLoading.value = true await demandApi.save({
await demandApi.save({ projectId,
projectId, round,
round, dut,
dut, designDemand,
designDemand, adequacy,
adequacy, ident,
ident, name,
name, testType,
testType, testMethod,
testMethod, testDesciption,
testDesciption, priority,
priority, testContent
testContent })
}) // 请求成功后需要清除dataList内容关闭弹窗提示新增成功刷新树状结构以及表格给父组件刷新
// 请求成功后需要清除dataList内容关闭弹窗提示新增成功刷新树状结构以及表格给父组件刷新 dataList.value = []
dataList.value = [] visible.value = false
visible.value = false Message.success("录入测试项成功")
Message.success("录入测试项成功") emit("updateTable")
emit("updateTable") } catch (e) {
} catch (e) { } finally {
} finally { luButtonLoading.value = false
luButtonLoading.value = false generateLoading.value = false
generateLoading.value = false }
} }
}
// 悬浮按钮显示上级设计需求内容
// 悬浮按钮显示上级设计需求内容 </script>
</script>
<style scoped lang="less">
<style scoped lang="less"> .index-hao {
.index-hao { font-size: 18px;
font-size: 18px; padding: 0 9px;
padding: 0 9px; color: rgb(var(--primary-5));
color: rgb(var(--primary-5)); }
} :deep(.arco-list-item) {
:deep(.arco-list-item) { border: 1px solid #999 !important;
border: 1px solid #999 !important; }
} :deep(.arco-progress-line-bar) {
:deep(.arco-progress-line-bar) { border-radius: 0 !important;
border-radius: 0 !important; }
} :deep(.arco-progress-line) {
:deep(.arco-progress-line) { border-radius: 0 !important;
border-radius: 0 !important; }
} .label {
.label { font-weight: 700;
font-weight: 700; }
} .luButton {
.luButton { margin-left: auto;
margin-left: auto; padding: 10px;
padding: 10px; padding-right: 0;
padding-right: 0; }
} </style>
</style>

View File

@@ -5,7 +5,7 @@ import caseApi from "@/api/project/case"
import { useTreeDataStore } from "@/store" import { useTreeDataStore } from "@/store"
import { isEqual, cloneDeep } from "lodash-es" import { isEqual, cloneDeep } from "lodash-es"
/** /**
* Dut被测件的crud选项 * 用例的Crud Init
*/ */
export default function (crudRef: Ref<InstanceType<typeof MaCrud>>) { export default function (crudRef: Ref<InstanceType<typeof MaCrud>>) {
// globals // globals
@@ -24,7 +24,6 @@ export default function (crudRef: Ref<InstanceType<typeof MaCrud>>) {
if (!beforeFormStep) { if (!beforeFormStep) {
return return
} }
crudRef.value.getFormData().testStep
const iuEqualValue = isEqual(crudRef.value.getFormData().testStep, beforeFormStep) const iuEqualValue = isEqual(crudRef.value.getFormData().testStep, beforeFormStep)
!iuEqualValue && !iuEqualValue &&
app.$modal.confirm({ app.$modal.confirm({

View File

@@ -39,17 +39,9 @@
@replaceSuccess="replaceSuccessHandle" @replaceSuccess="replaceSuccessHandle"
/> />
<!-- 批量替换人员组件 --> <!-- 批量替换人员组件 -->
<replace-person <replace-person :selectRows="undefined" @modify-success="crudRef.refresh()" ref="replacePersonModalRef" />
:selectRows="undefined"
@modify-success="crudRef.refresh()"
ref="replacePersonModalRef"
/>
<!-- 批量替换时间 --> <!-- 批量替换时间 -->
<replace-exetime <replace-exetime :selectRows="undefined" @modify-success="crudRef.refresh()" ref="replaceExetimeModalRef" />
:selectRows="undefined"
@modify-success="crudRef.refresh()"
ref="replaceExetimeModalRef"
/>
</div> </div>
</template> </template>

View File

@@ -214,6 +214,8 @@ const useCrudInit = function () {
hide: true, hide: true,
search: false, search: false,
formType: "select", formType: "select",
maxTagCount: 4,
multiple: true,
allowCreate: true, allowCreate: true,
createInfo: { createInfo: {
title: "运行环境" title: "运行环境"
@@ -259,6 +261,8 @@ const useCrudInit = function () {
hide: true, hide: true,
search: false, search: false,
formType: "select", formType: "select",
multiple:true,
maxTagCount: 4,
allowCreate: true, allowCreate: true,
createInfo: { createInfo: {
title: "开发环境" title: "开发环境"

View File

@@ -26,9 +26,7 @@
</a-space> </a-space>
</template> </template>
</a-popover> </a-popover>
<a-button @click="enterWorkPlant(record)" size="mini" status="warning" type="outline"> <a-button @click="enterWorkPlant(record)" size="mini" status="warning" type="outline"> 工作区 </a-button>
工作区
</a-button>
<a-link @click="previewRef.open(record)"><icon-eye />预览</a-link> <a-link @click="previewRef.open(record)"><icon-eye />预览</a-link>
<a-link @click="handleFragmentClick(record)"><icon-file />片段</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>