| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- <template>
- <div class="app-container">
- <el-row justify="space-between">
- <el-col :span="12">
- <el-form :model="queryParams" ref="queryRef" :inline="true" label-width="72px">
- <el-form-item label="容器名称">
- <el-input v-model="queryParams.containerName" placeholder="请输入容器名称" clearable style="width: 240px"
- @keyup.enter="getModelListTable"/>
- </el-form-item>
- <el-form-item>
- <el-button type="primary" icon="Search" @click="getModelListTable">查询</el-button>
- </el-form-item>
- </el-form>
- </el-col>
- <el-button type="primary" @click="reg" icon="Plus">创建容器</el-button>
- </el-row>
- <el-table
- :data="tableData"
- height="69vh"
- :cell-style="{ padding: '5px' }"
- :header-cell-style="{fontSize: '14px', height: heightAll * 0.01 + 'px' }"
- :row-style="{ fontSize: '1rem', textAlign:'center' }"
- border>
- <el-table-column type="index" label="序号" width="80">
- <template #default="{ $index }">
- <div style="text-align: center;">{{ $index + 1 }}</div>
- </template>
- </el-table-column>
- <el-table-column prop="containerName" label="模型名称"/>
- <el-table-column prop="name" label="容器名称"/>
- <el-table-column prop="imageName" label="容器镜像" width="220"/>
- <el-table-column prop="publishedPorts" label="开放端口" width="180">
- <template v-slot:header>
- 开放端口
- <el-popover title="开放端口" content="开放端口:内置端口" placement="top-start">
- <template #reference>
- <QuestionFilled style="width: 1em; height: 1em;"></QuestionFilled>
- </template>
- </el-popover>
- </template>
- </el-table-column>
- <el-table-column prop="status" align="center" label="模型状态" width="120">
- <template #default="scope">
- <el-tag v-if="scope.row.status==='RUNNING'">
- {{ getContainerStatus(scope.row.status) }}
- </el-tag>
- <el-tag v-if="scope.row.status==='STOPPED'" type="danger">
- {{ getContainerStatus(scope.row.status) }}
- </el-tag>
- <el-tag v-if="scope.row.status==='EXITED'" type="warning">
- {{ getContainerStatus(scope.row.status) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column align="center" label="操作" width="300">
- <template #default="scope">
- <div style="display: flex;justify-content: space-between;">
- <el-button type="primary" @click="handleEdit(scope.row)" size="small" text>编辑</el-button>
- <el-button v-if="scope.row.status!=='RUNNING'" @click="startContainers(scope.row.id)" type="primary"
- text
- size="small">运行
- </el-button>
- <el-button v-if="scope.row.status==='RUNNING'" @click="stopContainers(scope.row.id)" type="danger" text
- size="small">停止
- </el-button>
- <el-button v-if="scope.row.status==='RUNNING'" @click="restartContainers(scope.row.id)" type="warning"
- text
- size="small">重启
- </el-button>
- <el-button type="danger" @click="handleDelete(scope.row)" text size="small">删除</el-button>
- </div>
- </template>
- </el-table-column>
- </el-table>
- <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
- v-model:limit="queryParams.pageSize"/>
- <el-dialog v-model="dialogVisible" :title="dialogTitle" @close="clearForm" destroy-on-close>
- <el-form :model="form" label-position="right" ref="formRef" label-width="120px" :rules="rulesJi">
- <el-form-item label="容器名称:" prop="containerName">
- <el-input v-model="form.containerName"/>
- </el-form-item>
- <el-form-item label="容器英文名称:" prop="name">
- <el-input v-model="form.name" :disabled="form.id"/>
- </el-form-item>
- <el-form-item label="镜像:" prop="imageName">
- <el-select v-model="form.imageName" :disabled="form.id">
- <el-option v-for="item in imageList" :key="item.id" :label="item.imageName + ':' + item.tag"
- :value="item.imageName + ':' + item.tag"></el-option>
- </el-select>
- </el-form-item>
- <el-form-item>
- <template v-slot:label>
- <div style="display: flex;align-items: center;">
- 开放端口
- <el-popover title="开放端口" content="开放端口:内置端口" placement="top-start">
- <template #reference>
- <QuestionFilled style="width: 1em; height: 1em; margin: 0 5px;"></QuestionFilled>
- </template>
- </el-popover>
- :
- </div>
- </template>
- <el-input v-model="form.publishedPorts" :disabled="form.id"/>
- </el-form-item>
- <el-form-item label="容器说明">
- <el-input v-model="form.remark" type="textarea" placeholder="请输入容器说明"/>
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="dialogVisible = false">取消</el-button>
- <el-button type="primary" @click="submit">提交</el-button>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup>
- import {nextTick, onMounted, reactive, ref} from 'vue';
- import {getModelContainerById, getModelContainerPageList, saveModelContainer} from "@/api/container/modelContainer.js";
- import {deleteContainer, restartContainer, startContainer, stopContainer} from "@/api/container/containerOperation.js";
- import Pagination from "@/components/Pagination/index.vue";
- import {getModelContainerImageList} from "@/api/container/modelContainerImage.js";
- const {proxy} = getCurrentInstance();
- const queryParams = ref({
- pageNum: 1,
- pageSize: 20,
- containerName: '',
- })
- let tableData = ref([])
- const total = ref(0)
- function getContainerStatus(status) {
- switch (status) {
- case 'RUNNING':
- return '运行中'
- case 'STOPPED':
- return '已停止'
- case 'EXITED':
- return '审核未通过'
- default:
- return ''
- }
- }
- function startContainers(id) {
- startContainer(id).then(() => {
- proxy.$modal.msgSuccess('启动成功')
- getModelListTable()
- })
- }
- function stopContainers(id) {
- stopContainer(id).then(() => {
- proxy.$modal.msgSuccess('关闭成功')
- getModelListTable()
- })
- }
- function restartContainers(id) {
- restartContainer(id).then(() => {
- proxy.$modal.msgSuccess('重启成功')
- getModelListTable()
- })
- }
- async function handleEdit(row) {
- dialogVisible.value = true
- dialogTitle.value = `编辑【${row.containerName}】容器`
- await nextTick()
- getModelContainerById(row.id).then(res => {
- form.value = res.data
- })
- }
- function handleDelete(row) {
- proxy.$modal.confirm('是否确认删除?').then(function () {
- return deleteContainer(row.id);
- }).then(() => {
- getModelListTable();
- proxy.$modal.msgSuccess("删除成功");
- }).catch(() => {
- });
- }
- const dialogVisible = ref(false)
- const dialogTitle = ref('新建容器')
- const imageList = ref([])
- const formRef = ref();
- const form = ref({
- name: '',
- imageName: '',
- publishedPorts: '',
- });
- const rulesJi = reactive({
- name: [
- {required: true, message: '请输入容器名称', trigger: 'blur'},
- {
- pattern: /^[a-z][a-z0-9_-]{3,49}$/,
- message: '名称格式不正确,以字母开头,允许使用小写字母、数字、下划线和连字符。',
- trigger: 'blur'
- },
- ],
- imageName: [{required: true, message: '必填', trigger: 'blur'}],
- });
- const heightAll = window.innerHeight
- async function submit() {
- formRef.value.validate((valid) => {
- if (valid) {
- if (!form.value.id) {
- let [imageName, tag] = form.value.imageName.split(':');
- let image = imageList.value.find(item => item.imageName === imageName && item.tag === tag);
- if (image) {
- form.value.imageId = image.imageId
- }
- }
- saveModelContainer(form.value).then(() => {
- proxy.$modal.msgSuccess("保存成功");
- dialogVisible.value = false
- getModelListTable()
- })
- }
- })
- }
- function clearForm() {
- form.value = {
- name: '',
- imageName: '',
- publishedPorts: ''
- }
- }
- function reg() {
- dialogVisible.value = true
- }
- function getModelListTable() {
- getModelContainerPageList(queryParams.value).then(res => {
- tableData.value = res.rows
- total.value = res.total
- })
- }
- function getImageList() {
- getModelContainerImageList().then(res => {
- imageList.value = res.data
- })
- }
- getImageList()
- onMounted(() => {
- getModelListTable()
- });
- </script>
- <style scoped>
- .pagination-container {
- margin-top: 15px;
- }
- .type-container {
- padding-right: 10px;
- .type-item {
- padding: 10px;
- border-radius: 10px;
- text-align: center;
- background-color: #fff;
- color: #409EFF;
- cursor: pointer;
- &.active {
- background-color: #409EFF;
- color: #fff;
- }
- }
- }
- :deep(.treeLeft) .el-tree-node__content {
- display: flex !important;
- height: 28px; /* 按设计稿调整高度 */
- align-items: center;
- padding-top: 0 !important;
- }
- :deep(.treeLeft) .el-tree-node__content:hover {
- background-color: #e9e9eb;
- }
- :deep(.treeLeft) .el-tree-node__content:active {
- background-color: rgka(69, 157, 255, 0.1) !important;
- }
- /* 选中态(Active) */
- :deep(.treeLeft) .el-tree-node.is-current > .el-tree-node__content {
- background-color: #c6e2ff !important;
- }
- .tabs-wrapper {
- position: relative; /* 确保内容层在伪元素上方 */
- z-index: 1; /* 关键:高于背景图 */
- }
- .tabs-wrapper :deep(.el-tabs),
- .tabs-wrapper :deep(.el-tabs__content) {
- background-color: red !important;
- }
- :deep(.el-tabs) {
- background-color: transparent !important;
- }
- :deep(.el-tabs__content) {
- background-color: transparent !important;
- }
- :deep(.custom-dialog-bg) {
- z-index: 1000;
- background-image: url('@/assets/images/backDia.jpg') !important;
- background-position-x: left;
- background-position-y: bottom;
- background-size: initial;
- background-repeat: repeat-x;
- background-attachment: initial;
- background-origin: initial;
- background-clip: initial;
- background-color: rgb(255, 255, 255);
- }
- :deep(.custom-dialog-bg .el-dialog__header) {
- }
- :deep(.custom-dialog-bg .el-dialog__body) {
- color: #ecf0f1 !important;; /* 内容文字颜色 */
- }
- /* 横向排列单选框标签和输入框 */
- .custom-input-wrapper {
- display: flex;
- align-items: center;
- gap: 10px; /* 调整间距 */
- }
- /* 输入框仅显示底部横线 */
- .underline-input :deep(.el-input__wrapper) {
- padding: 0;
- box-shadow: none !important;
- border-bottom: 1px solid #dcdfe6; /* 横线颜色 */
- border-radius: 0;
- background: transparent;
- }
- .underline-input :deep(.el-input__inner) {
- height: 24px;
- padding: 0 5px;
- }
- :deep(.el-table__body tr:hover > td) {
- background-color: #eaf7ff !important;
- }
- .drag-handle {
- cursor: move;
- }
- .ghost {
- opacity: 0.5;
- background: #c8ebfb;
- }
- /* 防止文字选中 */
- :deep(.el-table__row) {
- user-select: none;
- -webkit-user-select: none;
- }
- </style>
- <style scoped lang="scss">
- .el-table .el-table__row td {
- height: 60px !important; /* 行高 */
- }
- .custom-tree-node {
- display: flex; /* 启用 Flex 布局 */
- align-items: center; /* 垂直居中 */
- gap: 8px; /* 图标与文字间距 */
- }
- :deep(.svg-icon) {
- outline: none;
- }
- :deep(.svg-icon svg) {
- stroke: none;
- }
- </style>
|