新增软件概述-段落和图片

This commit is contained in:
2026-01-31 17:34:03 +08:00
parent a5abc874ab
commit 72c080230f
11 changed files with 705 additions and 81 deletions

View File

@@ -5,31 +5,18 @@
<div class="logo-container">
<img src="@/assets/img/wxwx-logo.svg" class="logo" alt="logo" @click="handleClickLogo" />
</div>
<a-typography-title
class="title"
@click="handleClickLogo"
:style="{ margin: 0, fontSize: '18px' }"
:heading="5"
>
<div
class="font-extrabold bg-clip-text text-transparent bg-linear-to-r from-blue-500 to-purple-600"
>
测试管理平台
</div>
<a-typography-title class="title" @click="handleClickLogo" :style="{ margin: 0, fontSize: '18px' }" :heading="5">
<div class="font-extrabold bg-clip-text text-transparent bg-linear-to-r from-blue-500 to-purple-600">测试管理平台</div>
</a-typography-title>
<a-typography-title :heading="6" class="version">v{{ $version }}</a-typography-title>
<icon-menu-fold
v-if="!topMenu && appStore.device === 'mobile'"
style="font-size: 22px; cursor: pointer"
@click="toggleDrawerMenu"
/>
<icon-menu-fold v-if="!topMenu && appStore.device === 'mobile'" style="font-size: 22px; cursor: pointer" @click="toggleDrawerMenu" />
</a-space>
</div>
<div class="center-side flex items-center justify-center font-bold text-lg">
<template v-if="title">
<a-typography-title
:style="{ margin: 0, fontSize: '1.1rem', fontWeight: 'bold' }"
:heading="4"
:style="{ margin: 0, fontSize: '1.2rem', fontWeight: 'bold' }"
:heading="3"
:ellipsis="{
rows: 2
}"
@@ -102,6 +89,9 @@
</a-dropdown>
</li>
</ul>
<div class="fix-side" v-if="route.query.id">
<project-info-other />
</div>
</div>
</template>
@@ -114,6 +104,9 @@ import useUser from "@/hooks/logout"
import { Message } from "@arco-design/web-vue"
import Menu from "@/layout/components/menu.vue"
import { useRouter, useRoute } from "vue-router"
// 项目信息录入组件
import projectInfoOther from "./projectInfoOther/index.vue"
const router = useRouter()
const route = useRoute()
const appStore = useAppStore()
@@ -165,6 +158,14 @@ const handleClickLogo = () => {
</script>
<style scoped lang="less">
// 项目管理悬浮定位
.fix-side {
position: absolute;
left: 16%;
top: 50%;
transform: translateY(-50%);
}
.logo-container {
perspective: 1000px;
.logo {

View File

@@ -0,0 +1,40 @@
<template>
<div class="project-info-other-container">
<a-dropdown>
<a-button class="nav-btn">
<template #icon>
<icon-settings />
</template>
<a-space>
<span>项目设置</span>
<!-- 这里表示总体状态: not done -->
warning
</a-space>
</a-button>
<template #content>
<a-doption @click="ClickSoftSummary">软件概述</a-doption>
<a-doption>软件接口信息</a-doption>
</template>
</a-dropdown>
<project-modal ref="projectModalRef" />
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import ProjectModal from "./projectModal/index.vue"
// ref
const projectModalRef = ref<InstanceType<typeof ProjectModal> | null>(null)
// events
const ClickSoftSummary = async ()=>{
projectModalRef.value?.open()
}
defineOptions({
name: "ProjectInfoOther"
})
</script>
<style scoped lang="less"></style>

View File

@@ -0,0 +1,95 @@
<template>
<!-- 组件该组件显示一个正方体可粘贴图片内容进去并展示并生成base64到数据里面 -->
<div class="image-input-container flex flex-col gap-1">
<div class="image-input-handle flex items-center justify-center" @paste="handlePaste">
<!-- 加载状态 -->
<div v-if="isLoading">
<a-spin :size="32" />
</div>
<!-- 正常显示状态 -->
<template v-else-if="imgData">
<img :src="imgData" alt="粘贴的图片" class="preview-image img-container" />
</template>
<template v-else>
<div class="placeholder">请粘贴图片</div>
</template>
</div>
<!-- 题注fontnote -->
<a-input v-model="fontnote" class="max-w-100" placeholder="请输入题注"></a-input>
</div>
</template>
<script setup lang="ts">
import { Message } from "@arco-design/web-vue"
import { nextTick, ref } from "vue"
// 储存图片base64
const imgData = defineModel<string>()
// 储存题注
const fontnote = defineModel<string>("fontnote")
// 加载状态
const isLoading = ref(false)
// 处理粘贴事件
const handlePaste = async (e: ClipboardEvent) => {
e.preventDefault()
// 处理没有粘贴内容
if (!e.clipboardData) return
const items = e.clipboardData!.items
// 遍历粘贴板内容
for (let i = 0; i < items.length; i++) {
const item = items[i]
// 判断是否是粘贴的图片
if (item.kind === "file" && item.type.startsWith("image/")) {
const file = item.getAsFile()
if (!file) {
Message.error("读取图片失败,请重新粘贴")
break
}
// 判断大小不超过50M
if (file.size > 50 * 1024 * 1024) {
Message.error("要求图片不超过50M")
break
}
isLoading.value = true
const reader = new FileReader()
reader.onload = async (e: ProgressEvent<FileReader>) => {
imgData.value = e.target!.result as string
await nextTick() // 保证图片展示
isLoading.value = false
}
// 加载失败处理
reader.onerror = () => {
Message.error("图片加载失败,请重试")
isLoading.value = false
}
reader.readAsDataURL(file) // 可直接转为base64的url给img元素使用
break
} else {
Message.error("请粘贴图片,无法粘贴文字或其他内容")
break
}
}
}
</script>
<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: cover;
}
</style>

View File

@@ -0,0 +1,111 @@
import { TableColumnData } from "@arco-design/web-vue"
import { ref, reactive } from "vue"
// 粘贴图片组件
import ImageInput from "../ImageInput/index.vue"
// wordlike组件
import WordLikeTable from "../wordLikeTable/index.vue"
export default function useTable() {
const columns = reactive<TableColumnData[]>([
{
title: "类型",
width: 50,
align: "center",
dataIndex: "type",
render: ({ record }) => {
let item: string = "文"
let color: string = "red"
switch (record.type) {
case "text":
item = "文"
color = "blue"
break
case "image":
item = "图"
color = "red"
break
case "table":
item = "表"
color = "orangered"
break
}
return <a-tag color={color}>{item}</a-tag>
}
},
{
title: "内容",
dataIndex: "content",
slotName: "content",
render({ record, rowIndex }) {
const { type } = record
switch (type) {
case "text":
return <a-textarea v-model={data.value[rowIndex].content} placeholder="请输入段落" allow-clear />
case "image":
return <ImageInput v-model={data.value[rowIndex].content} v-model:fontnote={data.value[rowIndex].fontnote}></ImageInput>
case "table":
return <WordLikeTable></WordLikeTable>
}
}
},
{
title: "操作",
align: "center",
width: 80,
render: ({ rowIndex }) => {
return (
<a-space>
<a-tooltip content="注意:删除后数据丢失">
<a-button type="primary" status="danger" onClick={() => deleteRow(rowIndex)}>
{{ icon: () => <icon-delete /> }}
</a-button>
</a-tooltip>
</a-space>
)
}
}
])
// 数据定义 - 测试
const data = ref([
{
type: "text",
content: "这是数据内容",
fontnote: ""
}
])
// 单行初始内容-并设置数据类型
const initalRowData = {
type: "text",
content: "",
fontnote: ""
}
// 删除该行
const deleteRow = async (rowIndex: number) => {
data.value.splice(rowIndex, 1)
}
// 拖拽
const handleChange = (_data: typeof data.value) => {
data.value = _data
}
// 新增文
const addTextRow = () => {
data.value.push({ ...initalRowData })
}
// 新增图片
const addPicRow = () => {
data.value.push({ type: "image", content: "", fontnote: "" })
}
// 新增表格
const addTableRow = () => {
data.value.push({ type: "table", content: "", fontnote: "" })
}
return { columns, data, handleChange, addTextRow, addPicRow, addTableRow }
}

View File

@@ -0,0 +1,46 @@
<template>
<div class="project-modal-container">
<a-modal v-model:visible="visible" width="70%" draggable :on-before-ok="handleSyncOk">
<template #title>{{ "软件概述" }}</template>
<div class="mb-2">
<a-space>
<a-dropdown>
<a-button type="primary">新增元素<icon-plus /></a-button>
<template #content>
<a-doption @click="addTextRow">文字</a-doption>
<a-doption @click="addPicRow">图片</a-doption>
<a-doption @click="addTableRow">表格</a-doption>
</template>
</a-dropdown>
<a-alert type="warning" style="height: 32px">段落会在word渲染时自动缩进2个字符</a-alert>
</a-space>
</div>
<a-table :columns="columns" :show-header="false" :data="data" @change="handleChange" :draggable="{ type: 'handle', width: 40 }"> </a-table>
</a-modal>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue"
import useTable from "./hooks/useTable"
const visible = ref(false)
const { columns, data, handleChange, addTextRow, addPicRow, addTableRow } = useTable()
const handleSyncOk = async () => {
return false
}
const open = async () => {
visible.value = true
}
defineExpose({ open })
defineOptions({
name: "ProjectModal"
})
</script>
<style scoped></style>

View File

@@ -0,0 +1,14 @@
<template>
<!-- 完成自定义表格 -->
<div class="word-like-container">
111
</div>
</template>
<script setup lang="ts">
</script>
<style scoped lang="less">
</style>

View File

@@ -34,7 +34,6 @@ export default function useSearchNodes() {
const searchKey = ref("")
// 点击搜索事件
const handleSearchTreeDataClick = () => {
console.log(searchKey.value)
// 返回过滤后的treeData
// treeDataStore.originTreeData
if (searchKey.value) {