0.0.1版本

This commit is contained in:
2024-07-22 18:57:12 +08:00
parent e071bee532
commit bf0b0f6080
49 changed files with 868 additions and 499 deletions

View File

@@ -4,7 +4,11 @@
label="旧密码"
field="oldPassword"
label-col-flex="80px"
:rules="[{ required: true, message: '旧密码必填' }]"
:rules="[
{ required: true, message: '旧密码必填' },
{ minLength: 6, message: '密码长度至少6位' },
{ maxLength: 18, message: '密码最长18位' }
]"
>
<a-input-password v-model="password.oldPassword" allow-clear autocomplete="off" />
</a-form-item>
@@ -12,7 +16,11 @@
label="新密码"
field="newPassword"
label-col-flex="80px"
:rules="[{ required: true, message: '新密码必填' }]"
:rules="[
{ required: true, message: '新密码必填' },
{ minLength: 6, message: '密码长度至少6位' },
{ maxLength: 18, message: '密码最长18位' }
]"
>
<a-input-password
v-model="password.newPassword"
@@ -69,14 +77,13 @@ const modifyPassword = async (data) => {
Message.error("确认密码与新密码不一致")
return
}
Message.error("由于采用LDAP认证请联系内网管理员修改!")
// const response = await user.modifyPassword(data.values)
// if (response.success) {
// tool.local.clear()
// visible.value = true
// } else {
// Message.error(response.message)
// }
const response = await user.modifyPassword(data.values)
if (response.success) {
tool.local.clear()
visible.value = true
} else {
Message.error(response.message)
}
}
}

View File

@@ -9,9 +9,6 @@
<a-form-item label="手机" label-col-flex="80px">
<a-input v-model="userInfo.phone" allow-clear />
</a-form-item>
<a-form-item label="邮箱" label-col-flex="80px">
<a-input v-model="userInfo.email" allow-clear />
</a-form-item>
<a-form-item label="个人签名" label-col-flex="80px">
<a-textarea v-model="userInfo.introduction" :max-length="255" class="h-28" show-word-limit allow-clear />
</a-form-item>
@@ -21,7 +18,7 @@
</a-form>
</template>
<script setup>
<script setup lang="ts">
import { reactive } from "vue"
import { useUserStore } from "@/store"
import { Message } from "@arco-design/web-vue"
@@ -32,9 +29,7 @@ const userInfo = reactive({
...userStore.$state
})
const modifyInfo = async (data) => {
// 注意要用values
console.log(data.values)
Message.error("由于采用LDAP登录无法修改!")
const modifyInfo = async (data: any) => {
Message.warning("请联系内网管理员进行修改")
}
</script>

View File

@@ -82,6 +82,11 @@ const operationLogList = computed(() => {
return item
})
})
// 组件命名
defineOptions({
name: "usercenter"
})
</script>
<style scoped>

View File

@@ -6,8 +6,8 @@
<template #status="{ record }">
<a-switch
:checked-value="1"
unchecked-value="2"
@change="changeStatus($event, record.id)"
:unchecked-value="2"
:before-change="changeStatusWrapper(record)"
:default-checked="record.status == 1"
></a-switch>
</template>
@@ -22,10 +22,12 @@ import userApi from "@/api/system/user"
import user from "@/api/system/user"
import { Message } from "@arco-design/web-vue"
// 切换状态按钮
const changeStatus = async (e, id) => {
const res = await userApi.changeUserStatus({ user_status: e, userId: id })
if (res.data) {
Message.success(res.data === "1" ? "启用成功" : "禁用成功")
const changeStatusWrapper = (record) => {
return async function (newVal) {
const res = await userApi.changeUserStatus({ user_status: newVal, userId: record.id })
if (res.data) {
Message.success(res.data === "1" ? "启用成功" : "禁用成功")
}
}
}
// crud组件
@@ -104,13 +106,6 @@ const crudColumns = reactive([
search: true,
commonRules: [{ match: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码" }]
},
{
title: "邮箱",
align: "center",
dataIndex: "email",
width: 100,
commonRules: [{ type: "email", message: "请输入正确的邮箱" }]
},
{
title: "密码",
dataIndex: "password",
@@ -145,6 +140,10 @@ const crudColumns = reactive([
formType: "range"
}
])
defineOptions({
name: "usermanage"
})
</script>
<style lang="less" scoped></style>

View File

@@ -6,6 +6,10 @@
<script setup>
import Statistics from "./components/statistics.vue"
defineOptions({
name: "workplace"
})
</script>
<style lang="less" scoped></style>

View File

@@ -46,6 +46,10 @@ const crudColumns = ref([
commonRules: [{ required: true, message: "全称和描述必填" }]
}
])
defineOptions({
name: "abbreviation"
})
</script>
<style lang="less" scoped></style>

View File

@@ -61,7 +61,7 @@ const crudOptions = ref({
searchColNumber: 4,
tablePagination: false,
operationColumn: true,
showTools:false,
showTools: false
})
const crudColumns = ref([
@@ -108,8 +108,12 @@ const crudColumns = ref([
search: true,
formType: "range",
width: 180
},
}
])
defineOptions({
name: "dictmanage"
})
</script>
<style lang="less" scoped></style>

View File

@@ -30,21 +30,28 @@ const crudOptions = ref({
})
const crudColumns = ref([
{ title: "ID", dataIndex: "id", addDisplay: false, editDisplay: false, width: 50, hide: true },
{ title: "公司编号", align: "center", dataIndex: "key", search: true, width: 220, formType: "input-number" },
{ title: "公司编号", align: "center", dataIndex: "key", search: true, width: 70, formType: "input-number" },
{
title: "名称",
align: "center",
dataIndex: "name",
search: true,
width: 220,
width: 150,
commonRules: [{ required: true, message: "名称必填" }]
},
{
title: "简称",
align: "center",
dataIndex: "refer_name",
search: true,
commonRules: [{ required: true, message: "简称必填" }]
},
{
title: "法人",
align: "center",
dataIndex: "entrust_person",
search: true,
width: 220,
width: 90,
commonRules: [{ required: true, message: "法人必填" }]
},
{
@@ -52,7 +59,7 @@ const crudColumns = ref([
align: "center",
dataIndex: "addr",
search: true,
width: 200,
width: 150,
commonRules: [{ required: true, message: "公司地址必填" }]
}
])

View File

@@ -89,8 +89,8 @@ const router = useRouter()
const route = useRoute()
const userStore = useUserStore()
// 绑定登录form的数据
const form = reactive({ username: "superAdmin", password: "admin123", code: "" })
// const form = reactive({ username: "", password: "", code: "" })
// const form = reactive({ username: "superAdmin", password: "admin123", code: "" })
const form = reactive({ username: "", password: "", code: "" })
// 获取验证码dom && arco表单loading
const Verify = ref(null)
const loading = ref(null)
@@ -108,7 +108,7 @@ const handleSubmit = async ({ values, errors }) => {
if (login_res) {
const { redirect, ...otherQuery } = router.currentRoute.value.query
router.push({
name: redirect || "Workplace",
name: redirect || "workplace",
query: {
...otherQuery // 将退出时的查询参数放入,这样就不会错误
}

View File

@@ -81,6 +81,10 @@ const crudColumns = ref([
editDisplay: false
}
])
defineOptions({
name: "loginLog"
})
</script>
<style lang="less" scoped></style>

View File

@@ -50,6 +50,10 @@ const handleDeleteAllLogButton = async () => {
crudRef.value.refresh()
Message.success(res.message)
}
defineOptions({
name: "operationLog"
})
</script>
<style lang="less" scoped></style>

View File

@@ -85,6 +85,10 @@ const crudColumns = ref([
{ title: "请求参数", dataIndex: "request_body", hide: true, formType: "textarea" },
{ title: "返回信息", dataIndex: "json_result", hide: true, formType: "textarea" }
])
defineOptions({
name: "operations"
})
</script>
<style lang="less" scoped></style>

View File

@@ -84,7 +84,6 @@ const handleRelatedChange = async (record) => {
}
}
loading.value = false
// 更新树状目录中case显示“<”“@”“X”的更新
treeStore.updateCaseTreeData(res.data, route.query.id)
emits("relatedOrunrelated")
}
@@ -349,7 +348,7 @@ const columns = ref([
{
title: "问题描述",
hide: true,
search: true,
search: false,
dataIndex: "operation",
formType: "editor"
},
@@ -391,14 +390,26 @@ const columns = ref([
hide: true,
formType: "select",
commonRules: [{ required: true, message: "测试人员必填" }],
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "悬挂状态",
dataIndex: "hang",
search: false,
search: true,
formType: "select",
addDisplay: false,
editDisplay: false,
dict: {
data: [
{ label: "悬挂", value: "1" },
{ label: "已有关联", value: "2" }
]
},
align: "center",
customRender: ({ record }) => {
// 判断是否为悬挂状态record.hang[Boolean]
@@ -452,7 +463,12 @@ const columns = ref([
dataIndex: "verifyPerson",
formType: "select",
commonRules: [{ required: true, message: "回归人" }],
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "回归日期",

View File

@@ -0,0 +1,229 @@
<template>
<div class="problem-form-caintainer">
<a-modal
v-model:visible="visible"
:on-before-ok="handleBeforeOk"
ok-text="修改"
cancel-text="关闭"
width="950px"
draggable
>
<template #title>{{ props.title }}</template>
<ma-form v-model="form" :columns="columnsOptions" :options="options" ref="crudForm" />
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref, reactive } from "vue"
import { IProps } from "./problemFormTypes"
import { useRoute } from "vue-router"
import problemApi from "@/api/project/problem"
import { Message } from "@arco-design/web-vue"
const visible = ref<boolean>(false)
const route = useRoute()
const props = defineProps<IProps>()
const handleBeforeOk = async () => {
const validateRes = await crudForm.value.validateForm()
if (validateRes) {
return false
}
const res = await problemApi.update(form.value.id, { ...form.value, projectId: route.query.id })
if (res.success) {
Message.success("更新问题单成功")
}
}
// 初始化ma-form
const crudForm = ref<any>()
const form = ref<any>({})
const options = ref({
showButtons: false,
labelAlign: "center"
})
const columnsOptions = reactive([
{
formType: "grid-tailwind",
customClass: [],
colNumber: 2,
cols: [
{
formList: [
{
title: "问题名称",
dataIndex: "name",
rules: [{ required: true, message: "名称是必填" }]
},
{
title: "缺陷状态",
dataIndex: "status",
formType: "radio",
rules: [{ required: true, message: "缺陷状态必填" }],
dict: {
name: "problemStatu",
translation: true,
props: { label: "title", value: "key" }
}
},
{
title: "缺陷等级",
dataIndex: "grade",
formType: "radio",
rules: [{ required: true, message: "缺陷等级必填" }],
dict: {
name: "problemGrade",
translation: true,
props: { label: "title", value: "key" }
}
}
]
},
{
formList: [
{
title: "问题标识",
dataIndex: "ident",
disabled: true
},
{
title: "闭环方式",
dataIndex: "closeMethod",
formType: "checkbox",
dict: {
name: "closeMethod",
translation: true,
props: { label: "title", value: "key" }
}
},
{
title: "缺陷类型",
dataIndex: "type",
rules: [{ required: true, message: "缺陷类型必填" }],
formType: "radio",
dict: {
name: "problemType",
translation: true,
props: { label: "title", value: "key" }
}
}
]
}
]
},
{
title: "测试人员填写",
formType: "divider"
},
{
title: "问题描述",
dataIndex: "operation",
formType: "editor"
},
{
title: "问题影响",
dataIndex: "result",
formType: "textarea"
},
{
title: "研制方回复",
formType: "divider"
},
{
title: "原因分析",
dataIndex: "analysis",
formType: "editor"
},
{
title: "影响域分析",
dataIndex: "effect_scope",
formType: "textarea"
},
{
title: "改正措施",
dataIndex: "solve",
formType: "textarea"
},
{
title: "回归填写",
formType: "divider"
},
{
title: "回归结果",
dataIndex: "verify_result",
formType: "editor"
},
{
title: "人员信息",
formType: "divider"
},
{
formType: "grid-tailwind",
customClass: [],
colNumber: 2,
cols: [
{
formList: [
{
title: "测试人员",
dataIndex: "postPerson",
formType: "select",
rules: [{ required: true, message: "测试人员必填" }],
dict: {
url: "system/user/list",
translation: true,
params: { project_id: route.query.id },
props: { label: "name", value: "name" }
}
},
{
title: "开发人员",
dataIndex: "designerPerson",
formType: "input",
rules: [{ required: true, message: "开发人员必填" }]
},
{
title: "回归人员",
dataIndex: "verifyPerson",
formType: "select",
rules: [{ required: true, message: "回归人" }],
dict: {
url: "system/user/list",
translation: true,
params: { project_id: route.query.id },
props: { label: "name", value: "name" }
}
}
]
},
{
formList: [
{
title: "测试日期",
dataIndex: "postDate",
formType: "date"
},
{
title: "开发方日期",
dataIndex: "designDate",
formType: "date"
},
{
title: "回归日期",
dataIndex: "verifyDate",
formType: "date"
}
]
}
]
}
])
// 暴露属性和方法
const open = (problem: object) => {
visible.value = true
// 打开时初始化表单
form.value = problem
}
defineExpose({ open })
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,3 @@
export interface IProps {
title?: string
}

View File

@@ -60,7 +60,7 @@ const crudOptions = ref({
delete: { show: true, api: problemApi.delete },
operationColumnAlign: "center", // 操作列居中
// 列表选项卡配置
tabs:{},
tabs: {},
beforeOpenAdd: function () {
// 先判断是否已经有个问题单了,如果有则不让用户创建
if (crudRef.value.getTableData().length >= 1) {
@@ -108,7 +108,7 @@ const crudOptions = ref({
},
showIndex: false,
showTools: false,
operationColumnAlign:'center',
operationColumnAlign: "center",
rowSelection: { showCheckedAll: true },
searchColNumber: 3,
tablePagination: false,
@@ -311,36 +311,42 @@ const crudColumns = ref([
title: "问题描述",
hide: true,
search: true,
align: "center",
dataIndex: "operation",
formType: "editor",
formType: "editor"
},
{
title: "问题影响",
hide: true,
align: "center",
dataIndex: "result",
formType: "textarea",
formType: "textarea"
},
{
title: "原因分析",
hide: true,
align: "center",
dataIndex: "analysis",
formType: "editor",
formType: "editor"
},
{
title: "影响域分析",
hide: true,
align: "center",
dataIndex: "effect_scope",
formType: "textarea",
formType: "textarea"
},
{
title: "改正措施",
hide: true,
align: "center",
dataIndex: "solve",
formType: "textarea"
},
{
title: "回归结果",
hide: true,
align: "center",
dataIndex: "verify_result",
formType: "editor"
},
@@ -348,9 +354,15 @@ const crudColumns = ref([
title: "测试人员",
dataIndex: "postPerson",
search: true,
align: "center",
formType: "select",
commonRules: [{ required: true, message: "测试人员必填" }],
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "测试日期",
@@ -377,7 +389,12 @@ const crudColumns = ref([
dataIndex: "verifyPerson",
formType: "select",
commonRules: [{ required: true, message: "提单人必填" }],
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "回归日期",
@@ -386,6 +403,10 @@ const crudColumns = ref([
formType: "date"
}
])
defineOptions({
name: "case"
})
</script>
<style lang="less" scoped></style>

View File

@@ -15,6 +15,8 @@
关联测试项
</a-button>
</template>
<!-- 版本字段的插槽 -->
<template #inputPrepend-ident> XQ_XX_ </template>
</ma-crud>
</div>
<!-- 关联的modal组件 -->
@@ -41,7 +43,7 @@
<script setup>
import { ref, computed } from "vue"
import { useRoute, useRouter } from "vue-router"
import { useRoute } from "vue-router"
import testDemandApi from "@/api/project/testDemand"
import { useTreeDataStore } from "@/store"
import commonApi from "@/api/common"
@@ -50,7 +52,6 @@ import { Message } from "@arco-design/web-vue"
const treeDataStore = useTreeDataStore()
const route = useRoute()
const router = useRouter()
const crudRef = ref()
// 根据传参获取key分别为轮次、设计需求的key
const roundNumber = route.query.key.split("-")[0]
@@ -197,6 +198,7 @@ const crudOptions = ref({
searchColNumber: 3,
tablePagination: false,
operationColumn: true,
operationColumnAlign:'center',
formOption: {
width: 1200
}
@@ -205,7 +207,7 @@ const crudColumns = ref([
{
title: "ID",
align: "center",
width: 50,
hide: true,
dataIndex: "id"
},
{
@@ -217,7 +219,8 @@ const crudColumns = ref([
search: true,
validateTrigger: "blur",
placeholder: "请填写测试项的标识,注意标识不能重复",
commonRules: [{ required: true, message: "测试项标识必填" }]
commonRules: [{ required: true, message: "测试项标识必填" }],
openPrepend: true
},
{
title: "名称",
@@ -262,7 +265,7 @@ const crudColumns = ref([
return true
}
}
},
}
},
{
title: "测试手段",
@@ -339,6 +342,10 @@ const refreshCrudTable = () => {
crudRef.value.refresh()
}
defineExpose({ refreshCrudTable })
defineOptions({
name: "designDemand"
})
</script>
<style lang="less" scoped>

View File

@@ -20,18 +20,12 @@
@change="handleUploadChange"
></a-upload>
</div>
<a-alert :style="{ margin: '10px 0' }" type="warning"
>上传录入提示信息硬性要求本辅助程序根据GJB438C文档结构进行解析要求1.功能需求章节必须包含<span
<a-alert :style="{ margin: '10px 0' }" type="warning">
请去除需求规格说明文件中不必要的部分再来此处解析注意本系统不支持word中emf格式图片如果使用了visio等图片请<span
class="important-text"
>CSCI功能需求</span
><span class="important-text">CSCI能力需求</span>文字,且章节级别必须为<span class="important-text"
>2</span
>; 2.外部接口章节必须包含<span class="important-text">CSCI外部接口需求</span
>文字,且章节级别必须为<span class="important-text">2</span
>暂时只能解析这两个章节如果章节号有标识则使用<span class="important-text"
>RQGN01-UART-ZRZL</span
><span class="important-text">(RQGN01-UART-XBRCV)</span>包含在里面</a-alert
>
>在word变为普通图片上传</span
>
</a-alert>
<div class="operation-container">
<span :style="{ marginRight: '10px', fontWeight: 700 }">操作按钮:</span>
<a-space>
@@ -39,7 +33,7 @@
<a-button type="outline" status="warning" @click="handleResetData">重置数据</a-button>
</a-space>
</div>
<a-spin :loading="loading" tip="解析word完成需要时间渲染HTML元素..." :style="{ width: '100%' }">
<a-spin :loading="loading" tip="解析word完成正在渲染界面..." :style="{ width: '100%' }">
<div class="demand-container">
<a-list :data="htmlData" :pagination-props="{ defaultPageSize: 15, total: htmlData.length }">
<template #item="{ item, index }">
@@ -117,6 +111,7 @@ import dictApi from "@/api/system/dict"
import demandApi from "@/api/project/designDemand"
import { useRoute, useRouter } from "vue-router"
import { parseHtmlStringByDemandDut } from "@/views/project/dut/tools/parseHtmlString"
import { HtmlParser } from "@/views/project/dut/tools/parser"
import { useTreeDataStore } from "@/store"
// 其他初始化数据
const route = useRoute()
@@ -169,8 +164,8 @@ const handleRquest = function (options) {
// 已经上传到浏览器了,需要解析为列表
onSuccess(1)
const rawHtml = res.value
const finalData = parseHtmlStringByDemandDut(rawHtml)
// 将finalData赋值给响应式数据ref变量htmlData
const parser = new HtmlParser(rawHtml)
const finalData = parser.parseToArray()
htmlData.value = finalData
// ~~~~使用nextTick等待DOM更新完成~~~~
nextTick(() => {

View File

@@ -1,258 +0,0 @@
<template>
<div class="file-input-container">
<a-modal
v-model:visible="modalVisible"
width="80%"
title="设计需求批量写入"
:unmount-on-close="true"
:mask-closable="false"
ok-text="录入"
:esc-to-close="false"
:on-before-ok="handleModalSubmit"
>
<div class="uploadContainer">
<span :style="{ marginBottom: '10px', flex: '0 1 160px' }">上传设计需求.docx:</span>
<a-upload
:style="{ marginBottom: '10px' }"
:custom-request="handleRquest"
:limit="1"
accept=".docx"
@change="handleUploadChange"
></a-upload>
</div>
<a-alert :style="{ margin: '10px 0' }" type="warning"
>上传录入提示信息硬性要求本辅助程序根据GJB438C文档结构进行解析要求1.功能需求章节必须包含<span
class="important-text"
>CSCI功能需求</span
><span class="important-text">CSCI能力需求</span>文字,且章节级别必须为<span class="important-text"
>2</span
>; 2.外部接口章节必须包含<span class="important-text">CSCI外部接口需求</span
>文字,且章节级别必须为<span class="important-text">2</span
>暂时只能解析这两个章节如果章节号有标识则使用<span class="important-text"
>RQGN01-UART-ZRZL</span
><span class="important-text">(RQGN01-UART-XBRCV)</span>包含在里面</a-alert
>
<div class="operation-container">
<span :style="{ marginRight: '10px', fontWeight: 700 }">操作按钮:</span>
<a-space>
<a-button type="primary" @click="handleCreateAtLatest">新增一条</a-button>
<a-button type="outline" status="warning" @click="handleResetData">重置数据</a-button>
</a-space>
</div>
<a-spin :loading="loading" tip="解析word完成需要时间渲染HTML元素..." :style="{ width: '100%' }">
<div class="demand-container">
<Empty v-if="!htmlData.length"></Empty>
<a-collapse show-expand-icon destroy-on-hide v-show="htmlData.length">
<a-collapse-item v-for="(item, index) in htmlData" :key="index">
<template #header>
<a-input-group>
<a-input
placeholder="章节号"
v-model="item.chapter"
:style="{ width: '110px' }"
@click.stop.prevent
></a-input>
<a-input
placeholder="标题"
v-model="item.title"
:style="{ width: '300px' }"
@click.stop.prevent
></a-input>
<a-input
placeholder="标识"
v-model="item.ident"
:style="{ width: '200px' }"
@click.stop.prevent
></a-input>
<a-select
:style="{ width: '200px' }"
placeholder="请选择设计需求类型"
@click.stop.prevent
v-model="item.demandType"
>
<a-option v-for="it in demandType" :value="it.key">{{ it.title }}</a-option>
</a-select>
</a-input-group>
</template>
<div class="content">
<MaEditor v-model="item.content" />
</div>
<template #extra>
<a-button-group>
<a-button
type="primary"
status="success"
size="mini"
@click.stop.prevent="handledownCreate(index)"
>
+下方新增
</a-button>
<a-button
type="primary"
status="danger"
size="mini"
@click.stop.prevent="handleDelete(index)"
>
删除该条
</a-button>
</a-button-group>
</template>
</a-collapse-item>
</a-collapse>
</div>
</a-spin>
</a-modal>
</div>
</template>
<script setup>
import { ref, nextTick, watch } from "vue"
import mammoth from "mammoth"
import dictApi from "@/api/system/dict"
import demandApi from "@/api/project/designDemand"
import { useRoute, useRouter } from "vue-router"
import { parseHtmlStringByDemandDut } from "@/views/project/dut/tools/parseHtmlString"
import { useTreeDataStore } from "@/store"
// 其他初始化数据
const route = useRoute()
const router = useRouter()
const treeDataStore = useTreeDataStore()
// ~导入editor组件~tinymce
import MaEditor from "@/components/ma-editor/index.vue"
import { Message } from "@arco-design/web-vue"
// 单个设计需求录入模版
const templateDemandObj = {
chapter: "",
title: "",
ident: "",
demandType: "",
content: ""
}
// 先请求设计需求的dict然后给demandType选项
let demandType = []
const getDictDemandType = async function () {
const res = await dictApi.getDictByCode({ code: "demandType" })
demandType = res.data
}
getDictDemandType()
// 弹窗显示
const modalVisible = ref(false)
// 全部html数据-循环创建折叠项
const htmlData = ref([])
// 数据变化spin显示
const loading = ref(false)
// 当upload组件发生变化时候
const handleUploadChange = (files) => {
files.length ? (files.length = 1) : (htmlData.value = [])
}
// 上传行为函数
const handleRquest = function (options) {
const { onProgress, onError, onSuccess, fileItem } = options
onProgress(0.1)
// 让spin组件先转圈
loading.value = true
// 让Empty组件不显示
htmlData.value.push(templateDemandObj)
// 获取文件
let reader = new FileReader()
reader.readAsArrayBuffer(fileItem.file)
reader.onload = function (loadEvent) {
let arrayBuffer = loadEvent.target.result
mammoth
.convertToHtml({ arrayBuffer: arrayBuffer })
.then((res) => {
// 已经上传到浏览器了,需要解析为列表
onSuccess(1)
const rawHtml = res.value
const finalData = parseHtmlStringByDemandDut(rawHtml)
// 将finalData赋值给响应式数据ref变量htmlData
htmlData.value = finalData
// ~~~~使用nextTick等待DOM更新完成~~~~
nextTick(() => {
loading.value = false
})
})
.catch(function (error) {
console.log("处理错误导致失败,请检查前端代码")
console.log("错误如下:", error)
onError(error)
})
}
}
// 上方按钮:直接在最下新增一条
const handleCreateAtLatest = () => {
const newDemand = JSON.parse(JSON.stringify(templateDemandObj))
htmlData.value.push(newDemand)
}
// 上方按钮:重置数据,点击页面不卡段
const handleResetData = () => {
htmlData.value = []
}
// 点击单条右侧按钮:下方新增 - 深拷贝,并插入到下方
const handledownCreate = (index) => {
const newDemand = JSON.parse(JSON.stringify(templateDemandObj))
htmlData.value.splice(index + 1, 0, newDemand)
}
// 点击单条右侧按钮:删除
const handleDelete = (index) => {
htmlData.value.splice(index, 1)
}
// 打开弹窗并初始化form数据
const open = function () {
// 打开时候传入对象可初始化from
modalVisible.value = true
}
// 为了性能监听modal关闭则清空数据
watch(
() => modalVisible.value,
(newValue) => {
if (!newValue) {
htmlData.value = []
}
}
)
// 提交事件
const handleModalSubmit = async () => {
if (!htmlData.value.length) {
Message.error("请添加设计需求后再提交...")
return false
}
const res = await demandApi.multiSave({
projectId: route.query.id,
key: route.query.key,
data: htmlData.value
})
if (res.code === 200) {
treeDataStore.updateDesignDemandTreeData(res.data, route.query.id)
Message.success("批量新增设计需求成功...")
return true
}
return false
}
// 暴露该组件ref的方法
defineExpose({ open })
</script>
<style lang="less" scoped>
.uploadContainer {
display: flex;
align-items: center;
& span {
font-size: 1em;
font-weight: 700;
}
}
.demand-container {
width: 100%;
height: 55vh;
padding: 5px;
overflow: auto;
}
.operation-container {
margin-bottom: 5px;
}
.important-text {
font-weight: 700;
color: red;
}
</style>

View File

@@ -14,6 +14,9 @@
上传需求规格说明快捷录入
</a-button>
</template>
<!-- 字段的前缀后缀的插槽 -->
<!-- 版本字段的插槽 -->
<template #inputPrepend-ident> SJ-XX- </template>
</ma-crud>
</div>
<file-input-modal ref="fileInputRef"></file-input-modal>
@@ -119,6 +122,7 @@ const crudColumns = ref([
title: "ID",
align: "center",
width: 50,
hide: true,
dataIndex: "id",
commonRules: [{ required: true, message: "ID必填" }],
validateTrigger: "blur"
@@ -132,7 +136,8 @@ const crudColumns = ref([
search: true,
validateTrigger: "blur",
placeholder: "请输入文档中设计需求的标识",
help: '若不知道则填"无"或不填'
help: '若不知道则填"无"或不填',
openPrepend: true
},
{
title: "设需名称",
@@ -191,8 +196,7 @@ const crudColumns = ref([
{
title: "接口来源",
dataIndex: "source",
hide: true,
extra: "接口独有四个字段,决定大纲的接口列表信息"
hide: true
},
{
title: "目的地",
@@ -206,11 +210,10 @@ const crudColumns = ref([
{
title: "接口类型",
dataIndex: "type",
hide: true,
extra: "接口类型例如:网络"
hide: true
},
{
title: "接口协议",
title: "接口内容",
dataIndex: "protocal",
hide: true
}
@@ -232,6 +235,10 @@ const fileInputRef = ref(null)
const handleAddFileInputDemand = () => {
fileInputRef.value.open()
}
defineOptions({
name: "dut"
})
</script>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,174 @@
// 根据后端定义,不能更改的枚举
enum DemandType {
gn = "1",
xn = "2",
jk = "3",
kkx = "4",
aqx = "5",
other = "6"
}
// 数据每一项的结构
interface DemandObj {
chapter: string
title: string
ident?: string // 如果其标题有()则放入
demandType: DemandType
content: string
}
// 定义h元素对象接口
interface IHobj {
level: number // h1 -> 1
title: string // 标题文字
ident?: string // 如果有()则放入
index: number // 该标题的索引先map的时候不设置后续再排列
demandType: DemandType
}
// h元素对象带计算出的章节号的对象
interface IHobjWithChapter extends IHobj {
chapter: string
}
export class HtmlParser {
domArray: Element[] // 初始化得到一个元素的集合
hWithChapter: IHobjWithChapter[]
constructor(htmlText: string) {
const parser = new DOMParser()
const doc = parser.parseFromString(htmlText, "text/html")
this.domArray = Array.from(doc.body.children)
// 解析domArray将h元素加入
const HDomArray = this.domArray.filter((domItem) => domItem.tagName.startsWith("H"))
const storeArray: IHobj[] = []
HDomArray.forEach((it, i) => {
!it.textContent && (it.textContent = "")
// 这里判断是什么类型的设计需求 -> 后续可以添加
let type: DemandType = DemandType.gn
if (it.textContent.includes("接口")) {
type = DemandType.jk
} else if (it.textContent.includes("性能")) {
type = DemandType.xn
} else if (it.textContent.includes("可靠")) {
type = DemandType.kkx
} else if (it.textContent.includes("安全")) {
type = DemandType.aqx
}
// 获取章节号
const level = +it.tagName.slice(1)
// 1.判断当前的章节级别的前一个级别的level是否一样/小于/大于
let index = 0
if (i > 0) {
// 找上一个标题对象的level
if (level === storeArray[i - 1].level) {
index = storeArray[i - 1].index + 1
} else if (level < storeArray[i - 1].level) {
// 如果等级小于上一个标题对象,找storeArray里面level一样的长度
for (let j = storeArray.length - 1; j >= 0; j--) {
if (storeArray[j].level === level) {
index = storeArray[j].index + 1
break
}
}
}
}
storeArray.push({
level: level,
title: it.textContent,
ident: it.textContent.match(/[\(](.*?)[\)]/)?.[1],
index: index,
demandType: type
})
})
// 直接将storeArray计算出章节号
const chapterArray: any[] = storeArray.map((it, indx) => {
let str = it.index + 1 + ""
for (let i = it.level - 1; i > 0; i--) {
for (let j = indx - 1; j >= 0; j--) {
if (storeArray[j].level === i) {
str = storeArray[j].index + 1 + "." + str
break
}
}
}
return {
...it,
chapter: str
}
})
this.hWithChapter = chapterArray
}
/**
* 将所有Element元素遍历,输出列表格式
*/
parseToArray(): DemandObj[] {
const resArr: DemandObj[] = []
let index = 0
let currentHElement: IHobjWithChapter = this.hWithChapter[index]
let adapterIndex: number = 1
this.domArray.forEach((item) => {
// 1.如果循环到H元素将其存入
if (item.tagName.startsWith("H")) {
const text = item.textContent
if (text !== currentHElement.title) {
currentHElement = this.hWithChapter[index + 1]
index++
adapterIndex = 1
}
} else {
// 2.构造每一项放入 - chapter和content计算
let content = ""
let ident = currentHElement.ident
if (!ident) {
ident = ""
}
// 这里对图片进行处理
if (item.querySelector("img")) {
const img = item.querySelector("img")
if (img) {
const strblob = img.src + ""
const blob = strblob.split(",")[1]
content = `<img src="data:image/png;base64,${blob}" />`
}
ident += `-g${adapterIndex}`
adapterIndex++
resArr.push({
chapter: currentHElement.chapter,
title: currentHElement.title,
ident: ident,
demandType: currentHElement.demandType,
content
})
} else {
// 如果不是图片进行判断
if (item.textContent) {
if (item.tagName === "TABLE") {
content = item.outerHTML
ident += `-t${adapterIndex}`
adapterIndex++
}
if (item.tagName === "P" && !item.querySelector("img")) {
content = item.innerHTML.trim()
ident += `-p${adapterIndex}`
adapterIndex++
}
if (item.tagName === "OL" || item.tagName === "UL") {
content = item.innerHTML.trim()
ident += `-u${adapterIndex}`
adapterIndex++
}
// title要将括号去除
resArr.push({
chapter: currentHElement.chapter,
title: currentHElement.title,
ident: ident,
demandType: currentHElement.demandType,
content
})
}
}
}
})
return resArr
}
}

View File

@@ -50,6 +50,10 @@ const fetchProblemData = async (
}
}
fetchProblemData()
defineOptions({
name: "problem"
})
</script>
<style lang="less" scoped></style>

View File

@@ -2,7 +2,10 @@
<div class="ma-content-block lg:flex justify-between p-4">
<div class="lg:w-full w-full lg:ml-4 mt-5 lg:mt-0">
<!-- CRUD组件 -->
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef"></ma-crud>
<ma-crud :options="crudOptions" :columns="crudColumns" ref="crudRef">
<!-- 版本字段的插槽 -->
<template #inputPrepend-version> V </template>
</ma-crud>
</div>
</div>
</template>
@@ -81,6 +84,7 @@ const crudColumns = ref([
title: "ID",
width: 50,
align: "center",
hide: true,
dataIndex: "id",
commonRules: [{ required: true, message: "ID是必填" }],
validateTrigger: "blur"
@@ -152,7 +156,9 @@ const crudColumns = ref([
dataIndex: "version",
search: true,
commonRules: [{ required: true, message: "版本必填" }],
validateTrigger: "blur"
validateTrigger: "blur",
help: "填写不带V字符",
openPrepend: true
},
{
title: "用户标识",
@@ -271,6 +277,10 @@ const crudColumns = ref([
}
}
])
defineOptions({
name: "round"
})
</script>
<style lang="less" scoped></style>

View File

@@ -8,15 +8,18 @@
</template>
</ma-crud>
</div>
<problem-form ref="problemFormRef" :title="title"></problem-form>
</div>
</template>
<script setup lang="jsx">
import { ref } from "vue"
import { useRoute, useRouter } from "vue-router"
import { useRoute } from "vue-router"
import caseApi from "@/api/project/case"
import dictApi from "@/api/system/dict"
import { useTreeDataStore } from "@/store"
import ProblemForm from "@/views/project/case/components/ProblemForm.vue"
const problemFormRef = ref(null)
const title = ref("问题单表单")
const treeDataStore = useTreeDataStore()
const route = useRoute()
const roundNumber = route.query.key.split("-")[0]
@@ -37,7 +40,8 @@ const crudOptions = ref({
add: { show: true, api: caseApi.save, text: "新增用例" },
edit: { show: true, api: caseApi.update, text: "修改用例" },
delete: { show: true, api: caseApi.delete },
operationColumnAlign:'center',
operationColumnAlign: "center",
isDbClickEdit: false, // 关闭双击编辑
// 处理新增删除后树状图显示
beforeOpenAdd: function () {
let key_split = route.query.key.split("-")
@@ -142,6 +146,7 @@ const crudColumns = ref([
title: "ID",
width: 60,
align: "center",
hide: true,
dataIndex: "id",
fixed: "left"
},
@@ -152,8 +157,8 @@ const crudColumns = ref([
width: 140,
align: "center",
addDisabled: true,
addDefaultValue: route.query.key,
editDefaultValue: route.query.key,
addDefaultValue: "用例标识自动生成结构为YL_IO_XXXX_001",
editDefaultValue: "用例标识自动生成结构为YL_IO_XXXX_001",
editDisabled: true,
search: true,
validateTrigger: "blur"
@@ -243,15 +248,20 @@ const crudColumns = ref([
}
}
},
{
title: "设计人员",
width: 80,
dataIndex: "designPerson",
align: "center",
hide: true,
search: true,
formType: "select",
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "执行人员",
@@ -260,7 +270,12 @@ const crudColumns = ref([
align: "center",
search: true,
formType: "select",
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "审核人员",
@@ -269,7 +284,12 @@ const crudColumns = ref([
align: "center",
hide: true,
formType: "select",
dict: { url: "system/user/list", translation: true, props: { label: "name", value: "name" } }
dict: {
url: "system/user/list",
params: { project_id: route.query.id },
translation: true,
props: { label: "name", value: "name" }
}
},
{
title: "用例综述",
@@ -345,6 +365,28 @@ const crudColumns = ref([
commonRules: [{ required: true, message: "执行状态必填" }]
}
]
},
{
title: "关联问题",
dataIndex: "problem",
width: 120,
addDisplay: false,
editDisplay: false,
align: "center",
customRender: ({ record }) => {
if (record.problem) {
return (
<a-link
onClick={() => {
title.value = `PT_${route.query.ident}_${record.problem.ident.padStart(3, 0)}`
problemFormRef.value.open(record.problem)
}}
>{`PT_${route.query.ident}_${record.problem.ident.padStart(3, 0)}`}</a-link>
)
} else {
return "无问题单"
}
}
}
])
// 暴露刷新表格方法给外部
@@ -352,6 +394,10 @@ const refreshCrudTable = () => {
crudRef.value.refresh()
}
defineExpose({ refreshCrudTable })
defineOptions({
name: "testDemand"
})
</script>
<style lang="less" scoped></style>

View File

@@ -8,6 +8,10 @@ const router = useRouter()
const route = useRoute()
const gotoPath = route.params.path as string
router.replace({ path: gotoPath })
defineOptions({
name: "redirect"
})
</script>
<style lang="less" scoped></style>

View File

@@ -25,20 +25,46 @@
</a-timeline-item>
<div class="info">
<a-alert>测试人员填写的项目开始时间结束时间轮次时间均会影响生成文档的时间!</a-alert>
<a-button type="primary" @click="handleModalVisible">展示目前文档生成的日期</a-button>
</div>
</a-timeline>
</div>
<!-- a-modal组件展示生成文档的全部信息 -->
<a-modal v-model:visible="visible" hide-cancel :closable="false" width="auto">
<template #title> 生成文档时间一览表 </template>
<a-card
:style="{ width: '600px' }"
:title="item.title"
hoverable
v-for="(item, index) in timeList"
:key="item.title"
>
<p v-for="(value, key, idx) in item" class="flex">
<template v-if="key !== 'title'">
<span class="font-bold w-[300px]">{{ key }}</span>
<span class="">{{ value }}</span>
</template>
</p>
</a-card>
</a-modal>
</div>
</template>
<script setup>
import { useDocTimeShow } from "./useDocTimeShow"
// 在一开始就请求接口
// 1.定义props
const props = defineProps({
pInfo: {
type: Object,
required: true
},
projectId: {
type: String,
required: true
}
})
const { visible, handleModalVisible, timeList } = useDocTimeShow(props.projectId)
</script>
<style lang="less" scoped>
@@ -56,6 +82,10 @@ const props = defineProps({
position: absolute;
right: 30px;
top: 1rem;
button {
width: 180px;
border-radius: 0;
}
}
}
.a-col-title {

View File

@@ -0,0 +1,35 @@
import { ref, computed } from "vue"
import projectApi from "@/api/project/project"
export function useDocTimeShow(projectId: number) {
const visible = ref<boolean>(false)
const handleModalVisible = (): void => {
visible.value = true
}
// 获取时间模块
const timeTemp = ref<any[]>([])
const getTimeByBackend = async () => {
const res = await projectApi.getDocumentTimeShow(projectId)
timeTemp.value = res.data
}
getTimeByBackend()
const timeList = computed(() => {
timeTemp.value.forEach((item) => {
// 遍历所有属性,对值格式进行处理
Object.keys(item).forEach((key) => {
if (Array.isArray(item[key])) {
item[key] = item[key].join("~")
} else {
if (item[key].includes("年")) {
item[key] = item[key].replace('年','').replace('月','').replace('日','')
}
}
})
})
return timeTemp.value
})
return {
visible,
handleModalVisible,
timeList
}
}

View File

@@ -4,7 +4,7 @@
<!-- 返回前面一页的按钮以及项目整体情况 -->
<Title :pInfo="loadingData"></Title>
<!-- 时间线显示项目情况 -->
<time-line :pInfo="loadingData"></time-line>
<time-line :pInfo="loadingData" :projectId="route.params.projectId"></time-line>
<!-- 以轮次为合集展示需求下面的测试项数用例数测试类型下面测试项和用例数量 -->
<round-info v-for="item in loadingData.statistics" :data="item"></round-info>
</a-layout-content>
@@ -24,6 +24,10 @@ const fetchData = async () => {
return projectApi.getBoardInfo(route.params.projectId)
}
const { loadingData, isDataLoading } = useFetchData({}, fetchData)
defineOptions({
name: "projBoard"
})
</script>
<style lang="less" scoped>

View File

@@ -798,6 +798,10 @@ const crudColumns = ref([
}
}
])
defineOptions({
name: "projmanage"
})
</script>
<style lang="less" scoped>