Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
04340bf7
authored
Aug 07, 2026
by
renyizhao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
动态规格
parent
5d3142e4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
341 additions
and
217 deletions
+341
-217
src/views/compute/resourcespu/ResourceConfigSelectDialog.vue
+233
-0
src/views/compute/resourcespu/form/index.vue
+102
-95
src/views/compute/resourcespu/index.vue
+6
-122
No files found.
src/views/compute/resourcespu/ResourceConfigSelectDialog.vue
0 → 100644
View file @
04340bf7
<
template
>
<Dialog
:title=
"dialogTitle"
v-model=
"dialogVisible"
:width=
"1000"
>
<!-- 搜索区域 -->
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"82px"
>
<el-form-item
label=
"配置类别"
prop=
"configCategory"
>
<el-select
v-model=
"queryParams.configCategory"
placeholder=
"请选择配置类别"
clearable
class=
"!w-200px"
>
<el-option
v-for=
"dict in categoryOptions"
:key=
"dict.value"
:label=
"dict.label"
:value=
"dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"配置选项"
prop=
"configOption"
>
<el-input
v-model=
"queryParams.configOption"
placeholder=
"请输入配置选项"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-200px"
/>
</el-form-item>
<el-form-item>
<el-button
@
click=
"handleQuery"
><Icon
icon=
"ep:search"
class=
"mr-5px"
/>
搜索
</el-button>
<el-button
@
click=
"resetQuery"
><Icon
icon=
"ep:refresh"
class=
"mr-5px"
/>
重置
</el-button>
</el-form-item>
</el-form>
<!-- 规格配置列表 -->
<el-table
ref=
"tableRef"
v-loading=
"loading"
:data=
"list"
:stripe=
"true"
:show-overflow-tooltip=
"true"
row-key=
"id"
@
selection-change=
"handleSelectionChange"
height=
"400"
>
<el-table-column
type=
"selection"
width=
"55"
:reserve-selection=
"true"
/>
<el-table-column
label=
"配置类别"
align=
"center"
prop=
"configCategory"
min-width=
"160"
>
<template
#
default=
"scope"
>
<dict-tag
:type=
"DICT_TYPE.COMPUTE_RESOURCE_CONFIG_CATEGORY"
:value=
"scope.row.configCategory"
/>
</
template
>
</el-table-column>
<el-table-column
label=
"配置选项"
align=
"center"
prop=
"configOption"
min-width=
"180"
/>
<el-table-column
label=
"详情备注"
align=
"center"
prop=
"detailRemark"
min-width=
"200"
/>
</el-table>
<!-- 分页 -->
<Pagination
:total=
"total"
v-model:page=
"queryParams.pageNo"
v-model:limit=
"queryParams.pageSize"
@
pagination=
"getList"
class=
"mt-4"
/>
<!-- 底部按钮 -->
<
template
#
footer
>
<el-button
@
click=
"dialogVisible = false"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"handleConfirm"
>
确定选择 (
{{
selectedCount
}}
)
</el-button>
</
template
>
</Dialog>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
reactive
,
computed
,
watch
,
nextTick
}
from
'vue'
import
{
DICT_TYPE
,
getStrDictOptions
}
from
'@/utils/dict'
import
{
ResourceConfigApi
,
ResourceConfig
}
from
'@/api/compute/resourceconfig'
interface
Props
{
modelValue
:
boolean
title
?:
string
/** 已选中的规格 ID 列表(用于回显勾选状态) */
selectedIds
?:
number
[]
}
interface
Emits
{
(
e
:
'update:modelValue'
,
value
:
boolean
):
void
(
e
:
'confirm'
,
selections
:
ResourceConfig
[]):
void
}
const
props
=
withDefaults
(
defineProps
<
Props
>
(),
{
title
:
'选择规格'
,
selectedIds
:
()
=>
[]
})
const
emit
=
defineEmits
<
Emits
>
()
const
dialogVisible
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
val
)
=>
emit
(
'update:modelValue'
,
val
)
})
const
dialogTitle
=
computed
(()
=>
props
.
title
)
// 字典选项
const
categoryOptions
=
getStrDictOptions
(
DICT_TYPE
.
COMPUTE_RESOURCE_CONFIG_CATEGORY
)
const
loading
=
ref
(
false
)
const
list
=
ref
<
ResourceConfig
[]
>
([])
const
total
=
ref
(
0
)
const
tableRef
=
ref
()
// 跨分页的全量已选规格(key: id,value: 完整规格对象)
// 用于支持多选 + 翻页 + 取消勾选场景
const
allSelectedMap
=
reactive
(
new
Map
<
number
,
ResourceConfig
>
())
const
selectedCount
=
computed
(()
=>
allSelectedMap
.
size
)
const
queryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
configCategory
:
undefined
,
configOption
:
undefined
,
status
:
0
// 默认只查询启用的规格
})
const
queryFormRef
=
ref
()
/** 查询列表 */
const
getList
=
async
()
=>
{
loading
.
value
=
true
try
{
const
data
=
await
ResourceConfigApi
.
getResourceConfigPage
(
queryParams
)
list
.
value
=
data
.
list
total
.
value
=
data
.
total
// 数据加载完后回显已选中的项
await
nextTick
()
restoreSelection
()
}
finally
{
loading
.
value
=
false
}
}
/** 回显已选中的规格(跨分页) */
const
restoreSelection
=
()
=>
{
if
(
!
props
.
selectedIds
?.
length
||
!
tableRef
.
value
)
return
const
idSet
=
new
Set
(
props
.
selectedIds
)
for
(
const
row
of
list
.
value
)
{
if
(
idSet
.
has
(
row
.
id
))
{
tableRef
.
value
.
toggleRowSelection
(
row
,
true
)
}
}
}
/** 搜索按钮操作 */
const
handleQuery
=
()
=>
{
queryParams
.
pageNo
=
1
getList
()
}
/** 重置按钮操作 */
const
resetQuery
=
()
=>
{
queryFormRef
.
value
?.
resetFields
()
queryParams
.
status
=
0
handleQuery
()
}
/** 处理表格多选变化
* el-table 的 selection-change 只会带当前页可见的已选行;
* 这里用 allSelectedMap 维护所有分页的全量已选规格。
* 策略:把当前页 id 全部从 map 中移除,再把 rows(当前页最新已选)写入 map,
* 这样能正确处理「取消勾选」和「翻页」两种场景。
*/
const
handleSelectionChange
=
(
rows
:
ResourceConfig
[])
=>
{
const
currentPageIds
=
list
.
value
.
map
((
r
)
=>
r
.
id
)
const
visibleSelectedIds
=
new
Set
(
rows
.
map
((
r
)
=>
r
.
id
))
for
(
const
id
of
currentPageIds
)
{
if
(
!
visibleSelectedIds
.
has
(
id
))
{
allSelectedMap
.
delete
(
id
)
}
}
for
(
const
row
of
rows
)
{
allSelectedMap
.
set
(
row
.
id
,
row
)
}
}
/** 确认选择 - 提交全量已选(跨分页) */
const
handleConfirm
=
()
=>
{
emit
(
'confirm'
,
Array
.
from
(
allSelectedMap
.
values
()))
dialogVisible
.
value
=
false
}
/** 打开弹窗时加载数据
* 弹窗只展示启用规格,但 props.selectedIds 里可能既有启用的也有禁用的;
* 跨分页的已选规格在第一页打开时不会触发 selection-change,所以这里要主动
* 预加载所有已选的启用规格到 allSelectedMap,让底部计数从一开始就是全量。
*/
const
handleOpen
=
async
()
=>
{
allSelectedMap
.
clear
()
if
(
props
.
selectedIds
?.
length
)
{
const
results
=
await
Promise
.
all
(
props
.
selectedIds
.
map
((
id
)
=>
ResourceConfigApi
.
getResourceConfig
(
id
).
catch
(()
=>
null
))
)
for
(
const
cfg
of
results
)
{
// 只统计启用规格(弹窗只展示启用规格,禁用的不在计数范围内)
if
(
cfg
&&
cfg
.
status
!==
1
)
{
allSelectedMap
.
set
(
cfg
.
id
,
cfg
)
}
}
}
getList
()
}
// 监听弹窗打开
watch
(
()
=>
props
.
modelValue
,
(
val
)
=>
{
if
(
val
)
{
handleOpen
()
}
else
{
// 关闭时清空全量选择
allSelectedMap
.
clear
()
}
},
{
immediate
:
true
}
)
</
script
>
src/views/compute/resourcespu/form/index.vue
View file @
04340bf7
This diff is collapsed.
Click to expand it.
src/views/compute/resourcespu/index.vue
View file @
04340bf7
...
@@ -17,83 +17,6 @@
...
@@ -17,83 +17,6 @@
class=
"!w-240px"
class=
"!w-240px"
/>
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"CPU"
prop=
"cpu"
>
<el-select
v-model=
"queryParams.cpu"
placeholder=
"请选择CPU配置"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"option in resourceConfigStore.getOptionsByType('cpu')"
:key=
"option.id"
:label=
"option.configOption"
:value=
"option.configOption"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"GPU"
prop=
"gpu"
>
<el-select
v-model=
"queryParams.gpu"
placeholder=
"请选择GPU配置"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"option in resourceConfigStore.getOptionsByType('gpu')"
:key=
"option.id"
:label=
"option.configOption"
:value=
"option.configOption"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"内存"
prop=
"ram"
>
<el-select
v-model=
"queryParams.ram"
placeholder=
"请选择内存配置"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"option in resourceConfigStore.getOptionsByType('ram')"
:key=
"option.id"
:label=
"option.configOption"
:value=
"option.configOption"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"存储"
prop=
"storage"
>
<el-select
v-model=
"queryParams.storage"
placeholder=
"请选择存储配置"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"option in resourceConfigStore.getOptionsByType('storage')"
:key=
"option.id"
:label=
"option.configOption"
:value=
"option.configOption"
/>
</el-select>
</el-form-item>
<!--
<el-form-item
label=
"服务器ip"
prop=
"ip"
>
-->
<!--
<el-input-->
<!-- v-model="queryParams.ip"-->
<!-- placeholder="请输入服务器ip"-->
<!-- clearable-->
<!-- @keyup.enter="handleQuery"-->
<!-- class="!w-240px"-->
<!-- />-->
<!--
</el-form-item>
-->
<!--
<el-form-item
label=
"初始用户名"
prop=
"initUsername"
>
-->
<!--
<el-input-->
<!-- v-model="queryParams.initUsername"-->
<!-- placeholder="请输入初始用户名"-->
<!-- clearable-->
<!-- @keyup.enter="handleQuery"-->
<!-- class="!w-240px"-->
<!-- />-->
<!--
</el-form-item>
-->
<!--
<el-form-item
label=
"初始密码"
prop=
"initPassword"
>
-->
<!--
<el-input-->
<!-- v-model="queryParams.initPassword"-->
<!-- placeholder="请输入初始密码"-->
<!-- clearable-->
<!-- @keyup.enter="handleQuery"-->
<!-- class="!w-240px"-->
<!-- />-->
<!--
</el-form-item>
-->
<el-form-item
label=
"商品简介"
prop=
"intro"
>
<el-form-item
label=
"商品简介"
prop=
"intro"
>
<el-input
<el-input
v-model=
"queryParams.intro"
v-model=
"queryParams.intro"
...
@@ -133,21 +56,6 @@
...
@@ -133,21 +56,6 @@
/>
/>
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item
label=
"服务器所在地"
prop=
"location"
>
<el-select
v-model=
"queryParams.location"
placeholder=
"请选择服务器所在地"
clearable
class=
"!w-240px"
>
<el-option
v-for=
"option in resourceConfigStore.getOptionsByType('location')"
:key=
"option.id"
:label=
"option.configOption"
:value=
"option.configOption"
/>
</el-select>
</el-form-item>
<!--
<el-form-item
label=
"商品封面图"
prop=
"picUrl"
>
-->
<!--
<el-form-item
label=
"商品封面图"
prop=
"picUrl"
>
-->
<!--
<el-input-->
<!--
<el-input-->
<!-- v-model="queryParams.picUrl"-->
<!-- v-model="queryParams.picUrl"-->
...
@@ -216,7 +124,7 @@
...
@@ -216,7 +124,7 @@
>
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
</el-button>
<el-button
<
!--
<
el-button
type=
"success"
type=
"success"
plain
plain
@
click=
"handleExport"
@
click=
"handleExport"
...
@@ -224,7 +132,7 @@
...
@@ -224,7 +132,7 @@
v-hasPermi=
"['compute:resource-spu:export']"
v-hasPermi=
"['compute:resource-spu:export']"
>
>
<Icon
icon=
"ep:download"
class=
"mr-5px"
/>
导出
<Icon
icon=
"ep:download"
class=
"mr-5px"
/>
导出
</el-button>
</el-button>
-->
<el-button
<el-button
type=
"danger"
type=
"danger"
plain
plain
...
@@ -268,16 +176,11 @@
...
@@ -268,16 +176,11 @@
</
template
>
</
template
>
</el-table-column>
</el-table-column>
<!-- 硬件配置字段已隐藏 -->
<!-- 硬件配置字段已隐藏 -->
<!-- <el-table-column label="算力资源分类编号" align="center" prop="categoryId" />-->
<!-- <el-table-column label="算力资源分类编号" align="center" prop="categoryId" />-->
<el-table-column
label=
"算力资源分类"
align=
"center"
prop=
"categoryName"
min-width=
"150"
/>
<el-table-column
label=
"算力资源分类"
align=
"center"
prop=
"categoryName"
min-width=
"150"
/>
<el-table-column
label=
"规格"
min-width=
"240"
>
<el-table-column
label=
"规格"
min-width=
"240"
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
<el-tag
<el-tag
v-for=
"(value, key) in row.specMap"
:key=
"key"
size=
"small"
class=
"!mr-1"
>
v-for=
"(value, key) in row.specMap"
:key=
"key"
size=
"small"
class=
"!mr-1"
>
{{
key
}}
:
{{
value
}}
{{
key
}}
:
{{
value
}}
</el-tag>
</el-tag>
</
template
>
</
template
>
...
@@ -351,10 +254,8 @@ import download from '@/utils/download'
...
@@ -351,10 +254,8 @@ import download from '@/utils/download'
import
{
createImageViewer
}
from
'@/components/ImageViewer'
import
{
createImageViewer
}
from
'@/components/ImageViewer'
import
{
ResourceSpuApi
,
ResourceSpu
}
from
'@/api/compute/resourcespu'
import
{
ResourceSpuApi
,
ResourceSpu
}
from
'@/api/compute/resourcespu'
import
{
DICT_TYPE
,
getIntDictOptions
,
getStrDictOptions
}
from
'@/utils/dict'
import
{
DICT_TYPE
,
getIntDictOptions
,
getStrDictOptions
}
from
'@/utils/dict'
import
{
useResourceConfigStore
}
from
'@/store/modules/compute/hardwareConfig'
const
{
push
}
=
useRouter
()
const
{
push
}
=
useRouter
()
const
resourceConfigStore
=
useResourceConfigStore
()
/** 算力资源SPU表(基础配置信息) 列表 */
/** 算力资源SPU表(基础配置信息) 列表 */
defineOptions
({
name
:
'ResourceSpu'
})
defineOptions
({
name
:
'ResourceSpu'
})
...
@@ -370,20 +271,9 @@ const queryParams = reactive({
...
@@ -370,20 +271,9 @@ const queryParams = reactive({
pageNo
:
1
,
pageNo
:
1
,
pageSize
:
10
,
pageSize
:
10
,
name
:
undefined
,
name
:
undefined
,
cpu
:
undefined
,
gpu
:
undefined
,
ram
:
undefined
,
storage
:
undefined
,
ip
:
undefined
,
initUsername
:
undefined
,
initPassword
:
undefined
,
intro
:
undefined
,
intro
:
undefined
,
categoryId
:
undefined
,
categoryId
:
undefined
,
source
:
undefined
,
source
:
undefined
,
location
:
undefined
,
picUrl
:
undefined
,
sliderPicUrls
:
undefined
,
sales
:
undefined
,
status
:
undefined
,
status
:
undefined
,
createTime
:
[]
createTime
:
[]
})
})
...
@@ -485,15 +375,9 @@ const handleExport = async () => {
...
@@ -485,15 +375,9 @@ const handleExport = async () => {
/** 初始化 **/
/** 初始化 **/
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
try
{
try
{
// 并行加载数据
// 加载分类列表 + 列表数据
const
[
categoryResponse
]
=
await
Promise
.
all
([
const
[
categoryResponse
]
=
await
Promise
.
all
([
ResourceSpuApi
.
listSimpleCategory
(),
getList
()])
ResourceSpuApi
.
listSimpleCategory
(),
resourceConfigStore
.
loadConfigOptions
()
])
categoryList
.
value
=
categoryResponse
categoryList
.
value
=
categoryResponse
// 加载列表数据
getList
()
}
catch
(
error
)
{
}
catch
(
error
)
{
console
.
error
(
'初始化失败:'
,
error
)
console
.
error
(
'初始化失败:'
,
error
)
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment