466 lines
11 KiB
Vue
466 lines
11 KiB
Vue
<template>
|
||
<div class="asset-group-page">
|
||
<section class="ag-panel">
|
||
<div class="ag-head">
|
||
<h3 class="ag-title">资源组管理</h3>
|
||
<a-button type="primary" @click="openCreateDialog">新增资源组</a-button>
|
||
</div>
|
||
<div class="ag-filter">
|
||
<div class="ag-field">
|
||
<label>名称</label>
|
||
<a-input v-model="filters.name" placeholder="按名称过滤" />
|
||
</div>
|
||
<div class="ag-field">
|
||
<label>资源组编号</label>
|
||
<a-input v-model="filters.groupIdsText" placeholder="多个编号用英文逗号分隔" />
|
||
</div>
|
||
<div class="ag-field">
|
||
<label>资源组类型</label>
|
||
<a-select v-model="filters.groupType">
|
||
<a-option value="AIGC">AIGC(生成类)</a-option>
|
||
</a-select>
|
||
</div>
|
||
<div class="ag-field">
|
||
<label>排序字段</label>
|
||
<a-select v-model="filters.sortBy">
|
||
<a-option value="CreateTime">创建时间</a-option>
|
||
<a-option value="UpdateTime">更新时间</a-option>
|
||
</a-select>
|
||
</div>
|
||
<div class="ag-field">
|
||
<label>排序方向</label>
|
||
<a-select v-model="filters.sortOrder">
|
||
<a-option value="Desc">从新到旧</a-option>
|
||
<a-option value="Asc">从旧到新</a-option>
|
||
</a-select>
|
||
</div>
|
||
<div class="ag-actions">
|
||
<a-button type="primary" :loading="listLoading" @click="search(1)">查询</a-button>
|
||
<a-button @click="resetFilters">重置</a-button>
|
||
</div>
|
||
</div>
|
||
|
||
<a-spin :loading="listLoading">
|
||
<div class="ag-total">总数:{{ totalCount }}</div>
|
||
<div class="ag-table-wrap">
|
||
<table class="ag-table">
|
||
<colgroup>
|
||
<col class="ag-col-id" />
|
||
<col class="ag-col-name" />
|
||
<col class="ag-col-desc" />
|
||
<col class="ag-col-type" />
|
||
<col class="ag-col-project" />
|
||
<col class="ag-col-time" />
|
||
<col class="ag-col-time" />
|
||
<col class="ag-col-action" />
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th>编号</th>
|
||
<th>名称</th>
|
||
<th>描述</th>
|
||
<th>类型</th>
|
||
<th>项目名称</th>
|
||
<th>创建时间</th>
|
||
<th>更新时间</th>
|
||
<th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="item in items" :key="item.Id || item.id">
|
||
<td>{{ item.Id || item.id }}</td>
|
||
<td>{{ item.Name || item.name }}</td>
|
||
<td>{{ item.Description || item.description || '-' }}</td>
|
||
<td>{{ formatGroupTypeLabel(item) }}</td>
|
||
<td>{{ item.ProjectName || item.projectName || '-' }}</td>
|
||
<td>{{ item.CreateTime || item.createTime || '-' }}</td>
|
||
<td>{{ item.UpdateTime || item.updateTime || '-' }}</td>
|
||
<td>
|
||
<a-button
|
||
size="mini"
|
||
type="outline"
|
||
:loading="detailLoadingId === (item.Id || item.id)"
|
||
@click="getDetail(item)">
|
||
详情
|
||
</a-button>
|
||
</td>
|
||
</tr>
|
||
<tr v-if="!items.length">
|
||
<td colspan="8" class="ag-empty">暂无数据</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<div class="ag-pagination">
|
||
<a-pagination
|
||
:total="totalCount"
|
||
:current="filters.pageNumber"
|
||
:page-size="filters.pageSize"
|
||
show-total
|
||
show-jumper
|
||
show-page-size
|
||
:page-size-options="listPageSizeOptions"
|
||
@change="onGroupPageChange"
|
||
@page-size-change="onPageSizeChange" />
|
||
</div>
|
||
</a-spin>
|
||
</section>
|
||
|
||
<a-modal v-model:visible="detailVisible" title="资源组详情" :footer="false" width="680px">
|
||
<pre class="ag-detail">{{ prettyDetail }}</pre>
|
||
</a-modal>
|
||
<a-modal
|
||
v-model:visible="createVisible"
|
||
title="新增资源组"
|
||
width="520px"
|
||
:confirm-loading="createLoading"
|
||
@ok="createGroup">
|
||
<!-- 弹层 teleported 到 body,使用 FormItem 保证标签与 dark 样式生效 -->
|
||
<a-form :model="createForm" layout="horizontal" auto-label-width class="ag-create-modal-form">
|
||
<a-form-item label="名称" required>
|
||
<a-input
|
||
v-model="createForm.name"
|
||
placeholder="请输入资源组名称(≤64 字符)"
|
||
:max-length="64"
|
||
allow-clear />
|
||
</a-form-item>
|
||
<a-form-item label="描述">
|
||
<a-textarea
|
||
v-model="createForm.description"
|
||
:max-length="300"
|
||
show-word-limit
|
||
:auto-size="{ minRows: 3, maxRows: 8 }"
|
||
placeholder="请输入描述(≤300 字符)" />
|
||
</a-form-item>
|
||
</a-form>
|
||
</a-modal>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { byteApiItems, byteApiTotalCount } from '@/utils/byteAssetApi'
|
||
|
||
const LIST_PAGE_NUMBER_MAX = 100
|
||
const LIST_PAGE_SIZE_OPTIONS = [10, 20, 50, 100]
|
||
|
||
export default {
|
||
name: 'AssetGroupManage',
|
||
data() {
|
||
return {
|
||
createLoading: false,
|
||
createVisible: false,
|
||
listLoading: false,
|
||
detailLoadingId: '',
|
||
detailVisible: false,
|
||
detailData: null,
|
||
createForm: {
|
||
name: '',
|
||
description: '',
|
||
groupType: 'AIGC'
|
||
},
|
||
filters: {
|
||
name: '',
|
||
groupIdsText: '',
|
||
groupType: 'AIGC',
|
||
pageNumber: 1,
|
||
pageSize: 10,
|
||
sortBy: 'CreateTime',
|
||
sortOrder: 'Desc'
|
||
},
|
||
totalCount: 0,
|
||
items: [],
|
||
listPageSizeOptions: LIST_PAGE_SIZE_OPTIONS
|
||
}
|
||
},
|
||
computed: {
|
||
prettyDetail() {
|
||
return this.detailData ? JSON.stringify(this.detailData, null, 2) : '{}'
|
||
}
|
||
},
|
||
mounted() {
|
||
this.search(1)
|
||
},
|
||
methods: {
|
||
formatGroupTypeLabel(item) {
|
||
const t = String(item?.GroupType || item?.groupType || '').trim()
|
||
if (!t) return '-'
|
||
if (t === 'AIGC') return 'AIGC(生成类)'
|
||
return t
|
||
},
|
||
clampGroupPage(n) {
|
||
const page = Number(n) || 1
|
||
const size = Number(this.filters.pageSize) || 10
|
||
let maxPage = LIST_PAGE_NUMBER_MAX
|
||
if (this.totalCount > 0) {
|
||
const totalPages = Math.ceil(this.totalCount / size)
|
||
maxPage = Math.min(LIST_PAGE_NUMBER_MAX, Math.max(1, totalPages))
|
||
}
|
||
return Math.min(Math.max(1, page), maxPage)
|
||
},
|
||
onGroupPageChange(page) {
|
||
this.search(this.clampGroupPage(page))
|
||
},
|
||
openCreateDialog() {
|
||
this.createVisible = true
|
||
},
|
||
buildGroupIds() {
|
||
return String(this.filters.groupIdsText || '')
|
||
.split(',')
|
||
.map((x) => x.trim())
|
||
.filter((x) => !!x)
|
||
},
|
||
async createGroup() {
|
||
const name = String(this.createForm.name || '').trim()
|
||
if (!name) {
|
||
this.$message.error('请填写名称')
|
||
return
|
||
}
|
||
this.createLoading = true
|
||
try {
|
||
const res = await this.$axios({
|
||
url: 'api/byteAssetGroup/createAssetGroup',
|
||
method: 'POST',
|
||
data: {
|
||
name,
|
||
description: String(this.createForm.description || '').trim()
|
||
}
|
||
})
|
||
if (res.code === 200) {
|
||
this.$message.success('新增成功')
|
||
this.createForm.name = ''
|
||
this.createForm.description = ''
|
||
this.createVisible = false
|
||
this.search(1)
|
||
} else {
|
||
this.$message.error(res.msg || '新增失败')
|
||
}
|
||
} catch (e) {
|
||
this.$message.error(e?.message || '新增失败')
|
||
} finally {
|
||
this.createLoading = false
|
||
}
|
||
},
|
||
async search(page = this.filters.pageNumber) {
|
||
this.filters.pageNumber = this.clampGroupPage(page)
|
||
this.listLoading = true
|
||
try {
|
||
const payload = {
|
||
filter: {
|
||
groupType: this.filters.groupType || 'AIGC'
|
||
},
|
||
pageNumber: this.filters.pageNumber,
|
||
pageSize: this.filters.pageSize,
|
||
sortBy: this.filters.sortBy,
|
||
sortOrder: this.filters.sortOrder
|
||
}
|
||
const name = String(this.filters.name || '').trim()
|
||
if (name) payload.filter.name = name
|
||
const ids = this.buildGroupIds()
|
||
if (ids.length) payload.filter.groupIds = ids
|
||
|
||
const res = await this.$axios({
|
||
url: 'api/byteAssetGroup/listAssetGroups',
|
||
method: 'POST',
|
||
data: payload
|
||
})
|
||
const data = res.data || {}
|
||
this.totalCount = byteApiTotalCount(data)
|
||
this.items = byteApiItems(data)
|
||
if (res.code !== 200) {
|
||
this.$message.error(res.msg || '查询失败')
|
||
}
|
||
} catch (e) {
|
||
this.$message.error(e?.message || '查询失败')
|
||
} finally {
|
||
this.listLoading = false
|
||
}
|
||
},
|
||
onPageSizeChange(size) {
|
||
const s = Number(size) || 10
|
||
this.filters.pageSize = LIST_PAGE_SIZE_OPTIONS.includes(s) ? s : 10
|
||
this.search(1)
|
||
},
|
||
resetFilters() {
|
||
this.filters = {
|
||
name: '',
|
||
groupIdsText: '',
|
||
groupType: 'AIGC',
|
||
pageNumber: 1,
|
||
pageSize: 10,
|
||
sortBy: 'CreateTime',
|
||
sortOrder: 'Desc'
|
||
}
|
||
this.search(1)
|
||
},
|
||
async getDetail(item) {
|
||
const id = item?.Id || item?.id
|
||
if (!id) return
|
||
this.detailLoadingId = id
|
||
try {
|
||
const res = await this.$axios({
|
||
url: 'api/byteAssetGroup/getAssetGroup',
|
||
method: 'POST',
|
||
data: { id: id }
|
||
})
|
||
if (res.code === 200) {
|
||
this.detailData = res.data || {}
|
||
this.detailVisible = true
|
||
} else {
|
||
this.$message.error(res.msg || '查询详情失败')
|
||
}
|
||
} catch (e) {
|
||
this.$message.error(e?.message || '查询详情失败')
|
||
} finally {
|
||
this.detailLoadingId = ''
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
.asset-group-page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
padding: 18px;
|
||
background: #0a0b0d;
|
||
color: rgba(255, 255, 255, 0.88);
|
||
min-height: 100%;
|
||
}
|
||
|
||
.ag-panel {
|
||
background: rgba(22, 24, 30, 0.92);
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
border-radius: 14px;
|
||
padding: 14px;
|
||
}
|
||
|
||
.ag-title {
|
||
margin: 0;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
}
|
||
.ag-head {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 14px;
|
||
}
|
||
|
||
.ag-form,
|
||
.ag-filter {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, minmax(180px, 1fr));
|
||
gap: 12px;
|
||
}
|
||
.ag-field {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 6px;
|
||
}
|
||
|
||
.ag-field label {
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.65);
|
||
}
|
||
|
||
.ag-actions {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
gap: 8px;
|
||
}
|
||
|
||
.ag-total {
|
||
margin: 6px 0 10px;
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.65);
|
||
}
|
||
|
||
.ag-table-wrap {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
box-sizing: border-box;
|
||
overflow-x: auto;
|
||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.ag-table {
|
||
width: 100%;
|
||
max-width: 100%;
|
||
table-layout: fixed;
|
||
border-collapse: collapse;
|
||
font-size: 12px;
|
||
}
|
||
|
||
/* 列宽按百分比分配,随容器(100% 宽)随分辨率伸缩 */
|
||
.ag-col-id {
|
||
width: 10%;
|
||
}
|
||
.ag-col-name {
|
||
width: 12%;
|
||
}
|
||
.ag-col-desc {
|
||
width: 24%;
|
||
}
|
||
.ag-col-type {
|
||
width: 8%;
|
||
}
|
||
.ag-col-project {
|
||
width: 12%;
|
||
}
|
||
.ag-col-time {
|
||
width: 11%;
|
||
}
|
||
.ag-col-action {
|
||
width: 12%;
|
||
}
|
||
|
||
.ag-table th,
|
||
.ag-table td {
|
||
padding: 10px;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||
text-align: left;
|
||
vertical-align: top;
|
||
word-break: break-word;
|
||
overflow-wrap: anywhere;
|
||
}
|
||
|
||
.ag-table th {
|
||
color: rgba(255, 255, 255, 0.7);
|
||
font-weight: 600;
|
||
background: rgba(255, 255, 255, 0.02);
|
||
}
|
||
|
||
.ag-empty {
|
||
text-align: center;
|
||
color: rgba(255, 255, 255, 0.5);
|
||
}
|
||
|
||
.ag-pagination {
|
||
margin-top: 12px;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.ag-detail {
|
||
margin: 0;
|
||
padding: 12px;
|
||
border-radius: 8px;
|
||
background: rgba(0, 0, 0, 0.35);
|
||
color: #d8f4f7;
|
||
max-height: 420px;
|
||
overflow: auto;
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
}
|
||
|
||
@media (max-width: 960px) {
|
||
.ag-form,
|
||
.ag-filter {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
}
|
||
</style>
|