新增软件概述;新增全局loading插件
This commit is contained in:
16
cdTMP/package-lock.json
generated
16
cdTMP/package-lock.json
generated
@@ -32,7 +32,7 @@
|
||||
"vue": "^3.5.27",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-color-kit": "^1.0.6",
|
||||
"vue-data-ui": "^3.13.7",
|
||||
"vue-data-ui": "^3.14.1",
|
||||
"vue-router": "^5.0.1",
|
||||
"vuedraggable": "^2.24.3"
|
||||
},
|
||||
@@ -40,7 +40,7 @@
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/node": "^25.2.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qs": "^6.14.0",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
@@ -1900,9 +1900,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-25.1.0.tgz",
|
||||
"integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==",
|
||||
"version": "25.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/@types/node/-/node-25.2.0.tgz",
|
||||
"integrity": "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -5707,9 +5707,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/vue-data-ui": {
|
||||
"version": "3.13.7",
|
||||
"resolved": "https://registry.npmmirror.com/vue-data-ui/-/vue-data-ui-3.13.7.tgz",
|
||||
"integrity": "sha512-tFz+sN5CaDUl1/VotE35lV3hvJPurkhE6j/e3DldOMDbDCunOYvwqGuMglmGxQ8n/w1N/iqnEWVTmhIJHYdd8Q==",
|
||||
"version": "3.14.1",
|
||||
"resolved": "https://registry.npmmirror.com/vue-data-ui/-/vue-data-ui-3.14.1.tgz",
|
||||
"integrity": "sha512-i7GNaNtw39Avy9VuDHGdLdDpH2sRsr2ZFRpiilOvQ0XA5887KCvd7J5IUQRpVNTQSaT7pFSSEMPwcQ7NfPHuVw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"jspdf": ">=3.0.1",
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"vue": "^3.5.27",
|
||||
"vue-clipboard3": "^2.0.0",
|
||||
"vue-color-kit": "^1.0.6",
|
||||
"vue-data-ui": "^3.13.7",
|
||||
"vue-data-ui": "^3.14.1",
|
||||
"vue-router": "^5.0.1",
|
||||
"vuedraggable": "^2.24.3"
|
||||
},
|
||||
@@ -43,7 +43,7 @@
|
||||
"@tailwindcss/postcss": "^4.1.18",
|
||||
"@tailwindcss/vite": "^4.1.18",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^25.1.0",
|
||||
"@types/node": "^25.2.0",
|
||||
"@types/nprogress": "^0.2.3",
|
||||
"@types/qs": "^6.14.0",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
|
||||
@@ -110,5 +110,27 @@ export default {
|
||||
id: projectId
|
||||
}
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 新增或者修改软件概述
|
||||
* @returns 返回新增或修改是否成功
|
||||
*/
|
||||
postSoftSummary(data) {
|
||||
return request({
|
||||
url: "/testmanage/project/soft_summary/",
|
||||
method: "post",
|
||||
data: data
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 获取项目的软件概述
|
||||
* @returns 返回软件概述数据
|
||||
*/
|
||||
getSoftSummary(id) {
|
||||
return request({
|
||||
url: "/testmanage/project/get_soft_summary/",
|
||||
method: "get",
|
||||
params: { id: id }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
64
cdTMP/src/components/GlobalLoading/index.vue
Normal file
64
cdTMP/src/components/GlobalLoading/index.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div v-if="visible" class="global-loading">
|
||||
<div class="spinner"></div>
|
||||
<div v-if="text" class="loading-text">{{ text }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
|
||||
const visible = ref(false)
|
||||
const text = ref("加载中...")
|
||||
const show = (loadingText = "加载中...") => {
|
||||
text.value = loadingText
|
||||
visible.value = true
|
||||
}
|
||||
|
||||
const hide = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
// 暴露方法供外部调用
|
||||
defineExpose({ show, hide })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.global-loading {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
border: 4px solid rgba(0, 0, 0, 0.1);
|
||||
border-left-color: rgb(var(--blue-6));
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
margin-top: 10px;
|
||||
color: rgb(var(--blue-6));
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,5 @@
|
||||
import { TableColumnData } from "@arco-design/web-vue"
|
||||
import { ref, reactive } from "vue"
|
||||
import { ref, reactive, onUnmounted } from "vue"
|
||||
// 粘贴图片组件
|
||||
import ImageInput from "../ImageInput/index.vue"
|
||||
// wordlike组件
|
||||
@@ -44,7 +44,7 @@ export default function useTable() {
|
||||
case "image":
|
||||
return <ImageInput v-model={data.value[rowIndex].content} v-model:fontnote={data.value[rowIndex].fontnote}></ImageInput>
|
||||
case "table":
|
||||
return <WordLikeTable></WordLikeTable>
|
||||
return <WordLikeTable v-model={data.value[rowIndex].content} v-model:fontnote={data.value[rowIndex].fontnote}></WordLikeTable>
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -66,14 +66,19 @@ export default function useTable() {
|
||||
}
|
||||
])
|
||||
|
||||
// 数据定义 - 测试
|
||||
const data = ref([
|
||||
{
|
||||
type: "text",
|
||||
content: "这是数据内容",
|
||||
fontnote: ""
|
||||
// 卸载时清空数据
|
||||
const handleOnClose = () => {
|
||||
data.value = [{ ...initalRowData }]
|
||||
}
|
||||
])
|
||||
|
||||
// 数据定义 - 测试
|
||||
const data = ref<
|
||||
{
|
||||
type: string
|
||||
content: string | string[][]
|
||||
fontnote: string
|
||||
}[]
|
||||
>([])
|
||||
|
||||
// 单行初始内容-并设置数据类型
|
||||
const initalRowData = {
|
||||
@@ -104,8 +109,16 @@ export default function useTable() {
|
||||
|
||||
// 新增表格
|
||||
const addTableRow = () => {
|
||||
data.value.push({ type: "table", content: "", fontnote: "" })
|
||||
data.value.push({
|
||||
type: "table",
|
||||
content: [
|
||||
["", "", ""],
|
||||
["", "", ""],
|
||||
["", "", ""]
|
||||
],
|
||||
fontnote: ""
|
||||
})
|
||||
}
|
||||
|
||||
return { columns, data, handleChange, addTextRow, addPicRow, addTableRow }
|
||||
return { columns, data, handleChange, addTextRow, addPicRow, addTableRow, handleOnClose }
|
||||
}
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
<template>
|
||||
<div class="project-modal-container">
|
||||
<a-modal v-model:visible="visible" width="70%" draggable :on-before-ok="handleSyncOk">
|
||||
<template #title>{{ "软件概述" }}</template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
width="80%"
|
||||
draggable
|
||||
:on-before-ok="handleSyncOk"
|
||||
unmount-on-close
|
||||
ok-text="确认保存"
|
||||
cancel-text="关闭不保存"
|
||||
:maskClosable="false"
|
||||
@close="handleOnClose"
|
||||
>
|
||||
<template #title>{{ title }}</template>
|
||||
<div class="mb-2">
|
||||
<a-space>
|
||||
<a-dropdown>
|
||||
@@ -23,17 +33,44 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from "vue"
|
||||
import useTable from "./hooks/useTable"
|
||||
import projectApi from "@/api/project/project"
|
||||
import { useRoute } from "vue-router"
|
||||
import { Message } from "@arco-design/web-vue"
|
||||
import { getCurrentInstance } from "vue"
|
||||
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const visible = ref(false)
|
||||
const title = ref("软件概述-新增")
|
||||
|
||||
const { columns, data, handleChange, addTextRow, addPicRow, addTableRow } = useTable()
|
||||
const { columns, data, handleChange, addTextRow, addPicRow, addTableRow, handleOnClose } = useTable()
|
||||
|
||||
const handleSyncOk = async () => {
|
||||
try {
|
||||
await projectApi.postSoftSummary({ id: route.query.id, data: data.value })
|
||||
visible.value = false
|
||||
Message.success("保存成功")
|
||||
} catch (e) {
|
||||
Message.error("提交时发送错误,请联系管理员")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const open = async () => {
|
||||
proxy?.$loading?.show("数据加载中...")
|
||||
try {
|
||||
const res = await projectApi.getSoftSummary(route.query.id)
|
||||
const code = res.code // 25001表示有数据,25002表示没有数据
|
||||
title.value = code === 25001 ? "软件概述-修改" : "软件概述-新增"
|
||||
data.value = res.data
|
||||
visible.value = true
|
||||
} catch (e) {
|
||||
Message.error("获取软件概述信息失败")
|
||||
} finally {
|
||||
proxy?.$loading?.hide()
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
|
||||
@@ -1,14 +1,122 @@
|
||||
<template>
|
||||
<!-- 完成自定义表格 -->
|
||||
<div class="word-like-container">
|
||||
111
|
||||
<div class="fontnote">
|
||||
<a-space class="w-full">
|
||||
<span>题注:</span>
|
||||
<a-input v-model="fontnote" :style="{ width: '500px' }"></a-input>
|
||||
</a-space>
|
||||
</div>
|
||||
<div class="arco-table arco-table-size-large arco-table-border arco-table-stripe arco-table-hover">
|
||||
<div class="arco-table-container">
|
||||
<table class="arco-table-element" cellpadding="0" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="arco-table-tr">
|
||||
<th class="arco-table-th" v-for="(_, colIndex) in datas![0]" :key="colIndex">
|
||||
<span class="arco-table-cell items-center justify-center">
|
||||
<a-tooltip content="此列后新增列">
|
||||
<a-button type="text" size="mini" @click="addColumn(colIndex)" class="delete-col-btn">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip content="删除该列">
|
||||
<a-button
|
||||
type="text"
|
||||
size="mini"
|
||||
status="danger"
|
||||
@click="deleteColumn(colIndex)"
|
||||
:disabled="datas![0].length <= 1"
|
||||
class="delete-col-btn"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-close />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</th>
|
||||
<th class="arco-table-th" :style="{ textAlign: 'center' }">
|
||||
<span>操作</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="arco-table-tr" v-for="(row, rowIndex) in datas" :key="rowIndex">
|
||||
<td class="arco-table-td" v-for="(col, colIndex) in row" :key="colIndex">
|
||||
<span class="arco-table-cell">
|
||||
<a-textarea auto-size v-model="datas![rowIndex][colIndex]" />
|
||||
</span>
|
||||
</td>
|
||||
<td class="arco-table-td">
|
||||
<span class="arco-table-cell items-center justify-center">
|
||||
<a-tooltip content="此行后新增行">
|
||||
<a-button type="text" size="mini" @click="addRow(rowIndex)" class="delete-col-btn">
|
||||
<template #icon>
|
||||
<icon-plus />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip content="删除该行">
|
||||
<a-button size="mini" type="text" status="danger" @click="deleteRow(rowIndex)" :disabled="datas!.length <= 1">
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// 该组件储存数据
|
||||
const fontnote = defineModel<string>("fontnote")
|
||||
|
||||
const datas = defineModel<string[][]>()
|
||||
|
||||
// 行列操作
|
||||
const deleteRow = (rowIndex: number) => {
|
||||
datas.value!.splice(rowIndex, 1)
|
||||
}
|
||||
const deleteColumn = (colIndex: number) => {
|
||||
datas.value!.forEach((row) => {
|
||||
row.splice(colIndex, 1)
|
||||
})
|
||||
}
|
||||
const addRow = (rowIndex: number) => {
|
||||
const newRow = new Array(datas.value![0].length).fill("")
|
||||
datas.value!.splice(rowIndex + 1, 0, newRow)
|
||||
}
|
||||
const addColumn = (colIndex: number) => {
|
||||
// 处理空表格的特殊情况
|
||||
if (datas.value!.length === 0) {
|
||||
datas.value!.push([""])
|
||||
return
|
||||
}
|
||||
// 新增后续列
|
||||
datas.value!.forEach((row) => {
|
||||
const insertPosition = colIndex === -1 ? row.length : colIndex + 1
|
||||
row.splice(insertPosition, 0, "")
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.fontnote {
|
||||
margin: 10px 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.arco-textarea {
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.arco-table-cell {
|
||||
padding: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -25,6 +25,10 @@ app.use(router)
|
||||
app.use(pinia)
|
||||
app.use(globalComponents)
|
||||
|
||||
// 全局loading插件
|
||||
import loadingPlugin from "./plugins/loading"
|
||||
app.use(loadingPlugin)
|
||||
|
||||
// 使用服务端请求数据管理库
|
||||
import { VueQueryPlugin } from "@tanstack/vue-query"
|
||||
app.use(VueQueryPlugin)
|
||||
|
||||
16
cdTMP/src/plugins/loading.js
Normal file
16
cdTMP/src/plugins/loading.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { createVNode, render } from "vue"
|
||||
import LoadingComponent from "@/components/GlobalLoading/index.vue"
|
||||
|
||||
export default {
|
||||
install(app) {
|
||||
// 创建虚拟节点并渲染
|
||||
const vnode = createVNode(LoadingComponent)
|
||||
render(vnode, document.body)
|
||||
|
||||
// 挂载到全局属性
|
||||
app.config.globalProperties.$loading = {
|
||||
show: (text) => vnode.component?.exposed?.show(text),
|
||||
hide: () => vnode.component?.exposed?.hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user