826 lines
20 KiB
Vue
826 lines
20 KiB
Vue
<template>
|
||
<div class="image-form-container">
|
||
<!-- 顶部导航 -->
|
||
<div class="page-header">
|
||
<div class="header-left">
|
||
<el-button @click="goBack" class="back-btn" circle>
|
||
<el-icon><ArrowLeft /></el-icon>
|
||
</el-button>
|
||
<div class="header-title-area">
|
||
<h1 class="page-title">{{ isEdit ? '编辑镜像' : '添加镜像' }}</h1>
|
||
<span class="page-subtitle">{{ isEdit ? '修改现有镜像的配置信息' : '上传并配置新的虚拟机镜像' }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="header-actions">
|
||
<el-button @click="goBack" size="large">取消</el-button>
|
||
<el-button type="primary" @click="submitForm" :loading="submitting" size="large" class="submit-btn">
|
||
{{ isEdit ? '保存修改' : '立即创建' }}
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主表单区域 -->
|
||
<div class="form-wrapper">
|
||
<el-form :model="form" label-position="top" :rules="rules" ref="formRef" class="main-form" size="large">
|
||
|
||
<!-- 左侧:主要配置 -->
|
||
<div class="form-main-col">
|
||
<el-card class="premium-card" shadow="never">
|
||
<div class="section-header">
|
||
<div class="section-icon"><el-icon><Monitor /></el-icon></div>
|
||
<div class="section-info">
|
||
<h3>基础信息</h3>
|
||
<p>配置镜像的基本标识与文件信息</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid-2">
|
||
<el-form-item label="镜像名称" prop="name">
|
||
<el-input v-model="form.name" placeholder="请输入镜像名称" />
|
||
</el-form-item>
|
||
<el-form-item label="展示名称" prop="show_name">
|
||
<el-input v-model="form.show_name" placeholder="请输入展示名称" />
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<el-form-item label="文件路径" prop="path">
|
||
<el-input v-model="form.path" placeholder="请输入镜像文件在服务器上的绝对路径">
|
||
<template #prefix><el-icon><Folder /></el-icon></template>
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="镜像描述" prop="description">
|
||
<el-input
|
||
v-model="form.description"
|
||
type="textarea"
|
||
:rows="3"
|
||
placeholder="请输入关于此镜像的详细描述"
|
||
resize="none"
|
||
/>
|
||
</el-form-item>
|
||
|
||
<el-divider />
|
||
|
||
<div class="section-header">
|
||
<div class="section-icon"><el-icon><SetUp /></el-icon></div>
|
||
<div class="section-info">
|
||
<h3>分类与版本</h3>
|
||
<p>管理镜像的分类归属与版本信息</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-grid-2">
|
||
<el-form-item label="所属分类" prop="class_id">
|
||
<el-select
|
||
v-model="form.class_id"
|
||
placeholder="请选择分类"
|
||
clearable
|
||
style="width: 100%"
|
||
@change="handleCategoryChange"
|
||
>
|
||
<el-option v-for="item in categoryList" :key="item.class_id" :label="item.name" :value="item.class_id" />
|
||
<el-option label="+ 创建新分类" value="" class="create-new-option" />
|
||
</el-select>
|
||
|
||
<div class="new-category-input" v-if="showNewCategoryInput">
|
||
<el-input
|
||
v-model="form.class_name"
|
||
placeholder="输入新分类名称"
|
||
>
|
||
<template #append>
|
||
<el-button @click="createNewCategory">创建</el-button>
|
||
</template>
|
||
</el-input>
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<el-form-item label="版本号" prop="vm_gen">
|
||
<el-input v-model="form.vm_gen" placeholder="例如:v1.0.0" />
|
||
</el-form-item>
|
||
</div>
|
||
|
||
<el-form-item label="关联套餐" prop="plan_id">
|
||
<el-select v-model="form.plan_id" placeholder="请选择适用的套餐" style="width: 100%">
|
||
<el-option v-for="item in planList" :key="item.id" :label="item.name" :value="item.id" />
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-card>
|
||
</div>
|
||
|
||
<!-- 右侧:图标与高级 -->
|
||
<div class="form-side-col">
|
||
<el-card class="premium-card" shadow="never">
|
||
<div class="section-header small">
|
||
<div class="section-info">
|
||
<h3>镜像图标</h3>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="icon-uploader">
|
||
<div class="icon-preview" v-if="form.image_ico">
|
||
<img :src="mainUrl + form.image_ico" />
|
||
<div class="icon-actions">
|
||
<el-button size="small" circle @click="form.image_ico = ''"><el-icon><Delete /></el-icon></el-button>
|
||
</div>
|
||
</div>
|
||
<div class="upload-area" v-else>
|
||
<div class="upload-placeholder">
|
||
<el-icon class="upload-icon"><Picture /></el-icon>
|
||
<div class="upload-text">点击上传或选择图标</div>
|
||
</div>
|
||
<div class="upload-buttons">
|
||
<el-button type="primary" size="small" @click="$refs.fileInput.click()">本地上传</el-button>
|
||
<el-button size="small" @click="openPicLibrary">素材库</el-button>
|
||
</div>
|
||
<input ref="fileInput" type="file" style="display: none" @change="onFileSelected" accept="image/*" />
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
|
||
|
||
</div>
|
||
|
||
</el-form>
|
||
</div>
|
||
|
||
<!-- 素材库对话框 -->
|
||
<el-dialog v-model="picSwitch" title="选择图标" width="800px" append-to-body>
|
||
<div class="pic-search">
|
||
<el-input
|
||
v-model="picPagin.key"
|
||
placeholder="搜索图标..."
|
||
prefix-icon="Search"
|
||
clearable
|
||
@change="getpicList"
|
||
/>
|
||
</div>
|
||
<div class="pic-grid" v-loading="picLoading">
|
||
<div
|
||
v-for="(item, index) in picList"
|
||
:key="index"
|
||
class="pic-item"
|
||
:class="{ active: currentIndex === index }"
|
||
@click="selectImage(index)"
|
||
>
|
||
<img :src="`${mainUrl}/v1/attachment/get_attachment?aid=${item.attachment_id}`" />
|
||
<div class="pic-name">{{ item.title || '未命名' }}</div>
|
||
<div class="pic-check" v-if="currentIndex === index"><el-icon><Check /></el-icon></div>
|
||
</div>
|
||
</div>
|
||
<div class="pagination-wrapper">
|
||
<el-pagination
|
||
background
|
||
layout="prev, pager, next"
|
||
:total="total"
|
||
:current-page="picPagin.page"
|
||
:page-size="picPagin.count"
|
||
@current-change="CurrentPageChange"
|
||
/>
|
||
</div>
|
||
<template #footer>
|
||
<el-button @click="picSwitch = false">取消</el-button>
|
||
<el-button type="primary" @click="confirmPicSelection" :disabled="currentIndex === null">确定选择</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { ElMessage, ElNotification } from 'element-plus'
|
||
import {
|
||
ArrowLeft, Monitor, Folder, SetUp, Picture, Delete,
|
||
Search, Check
|
||
} from '@element-plus/icons-vue'
|
||
import { getServerPlan } from '@/utils/acs/server'
|
||
import {
|
||
editMirror, addVirtualMirror, getImageTypeList, createImageType, getUserMirrorList
|
||
} from '@/utils/acs/mirror'
|
||
import { uploadFile, getFileList } from '@/utils/acs/message'
|
||
import { mainUrl } from '@/utils/request'
|
||
|
||
import { useTagsViewStore } from '@/store/tagsViewStore'
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const tagsViewStore = useTagsViewStore()
|
||
const formRef = ref(null)
|
||
const submitting = ref(false)
|
||
|
||
const goBack = () => {
|
||
tagsViewStore.delVisitedView(route)
|
||
router.back()
|
||
}
|
||
const isEdit = computed(() => !!route.query.id)
|
||
const serverId = computed(() => route.query.server_id)
|
||
|
||
const form = reactive({
|
||
id: '',
|
||
name: '',
|
||
show_name: '',
|
||
description: '',
|
||
server_type: 'hyperV',
|
||
plan_id: '',
|
||
image_ico: '',
|
||
server_id: '',
|
||
path: '',
|
||
class_id: '',
|
||
class_name: '',
|
||
vm_gen: ''
|
||
})
|
||
|
||
const rules = {
|
||
name: [{ required: true, message: '请输入镜像名称', trigger: 'blur' }],
|
||
path: [{ required: true, message: '请输入文件路径', trigger: 'blur' }],
|
||
show_name: [{ required: true, message: '请输入展示名称', trigger: 'blur' }]
|
||
}
|
||
|
||
const categoryList = ref([])
|
||
const planList = ref([])
|
||
const showNewCategoryInput = ref(false)
|
||
|
||
// 素材库相关
|
||
const picSwitch = ref(false)
|
||
const picLoading = ref(false)
|
||
const picPagin = reactive({
|
||
count: 10,
|
||
page: 1,
|
||
key: '',
|
||
user_type: 1
|
||
})
|
||
const picList = ref([])
|
||
const total = ref(0)
|
||
const currentIndex = ref(null)
|
||
|
||
// 重置表单
|
||
const resetForm = () => {
|
||
Object.assign(form, {
|
||
id: '',
|
||
name: '',
|
||
show_name: '',
|
||
description: '',
|
||
server_type: 'hyperV',
|
||
plan_id: '',
|
||
image_ico: '',
|
||
server_id: '',
|
||
path: '',
|
||
class_id: '',
|
||
class_name: '',
|
||
vm_gen: ''
|
||
})
|
||
}
|
||
|
||
// 初始化数据
|
||
const initData = async () => {
|
||
resetForm()
|
||
|
||
if (!serverId.value) {
|
||
ElMessage.error('缺少服务器ID参数')
|
||
return
|
||
}
|
||
|
||
form.server_id = serverId.value
|
||
|
||
try {
|
||
// 获取套餐列表
|
||
const planRes = await getServerPlan({ server_id: serverId.value })
|
||
if (planRes.data.code === 200) {
|
||
planList.value = planRes.data.data.map(item => ({
|
||
name: item.name,
|
||
id: item.plan_id
|
||
}))
|
||
}
|
||
|
||
// 获取分类列表
|
||
await fetchCategoryList()
|
||
|
||
// 如果是编辑模式,填充数据
|
||
if (isEdit.value) {
|
||
const id = route.query.id
|
||
// 尝试从 history.state 获取数据
|
||
const stateData = history.state.params ? JSON.parse(JSON.stringify(history.state.params)) : null
|
||
|
||
if (stateData && stateData.id == id) {
|
||
Object.keys(form).forEach(key => {
|
||
if (key in stateData) {
|
||
form[key] = stateData[key]
|
||
}
|
||
})
|
||
// 确保 ID 类型一致性 (有些时候 API 返回的是数字,有些时候是字符串)
|
||
if (form.plan_id) form.plan_id = Number(form.plan_id) || form.plan_id
|
||
if (form.class_id) form.class_id = Number(form.class_id) || form.class_id
|
||
} else {
|
||
// Fallback: fetch list and find item
|
||
const listRes = await getUserMirrorList({
|
||
server_id: serverId.value,
|
||
count: 10,
|
||
page: 1
|
||
})
|
||
if (listRes.data.code === 200) {
|
||
const found = listRes.data.data.find(item => item.id == id)
|
||
if (found) {
|
||
Object.keys(form).forEach(key => {
|
||
if (key in found) form[key] = found[key]
|
||
})
|
||
if (form.plan_id) form.plan_id = Number(form.plan_id) || form.plan_id
|
||
if (form.class_id) form.class_id = Number(form.class_id) || form.class_id
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('初始化数据失败:', error)
|
||
ElMessage.error('数据加载失败')
|
||
}
|
||
}
|
||
|
||
const fetchCategoryList = async () => {
|
||
try {
|
||
const res = await getImageTypeList(serverId.value)
|
||
if (res.data.code === 200) {
|
||
categoryList.value = res.data.data || []
|
||
}
|
||
} catch (error) {
|
||
console.error('获取分类失败:', error)
|
||
}
|
||
}
|
||
|
||
const handleCategoryChange = (val) => {
|
||
if (val === '') {
|
||
// 选择了创建新分类
|
||
form.class_id = ''
|
||
showNewCategoryInput.value = true
|
||
} else {
|
||
showNewCategoryInput.value = false
|
||
form.class_name = ''
|
||
}
|
||
}
|
||
|
||
const createNewCategory = async () => {
|
||
if (!form.class_name.trim()) {
|
||
ElMessage.warning('请输入分类名称')
|
||
return
|
||
}
|
||
try {
|
||
const res = await createImageType(serverId.value, form.class_name.trim(), '')
|
||
if (res.data.code === 200) {
|
||
ElMessage.success('分类创建成功')
|
||
await fetchCategoryList()
|
||
// 选中新创建的分类
|
||
const newCat = categoryList.value.find(c => c.name === form.class_name.trim())
|
||
if (newCat) {
|
||
form.class_id = newCat.class_id
|
||
showNewCategoryInput.value = false
|
||
form.class_name = ''
|
||
}
|
||
} else {
|
||
ElMessage.error(res.data.msg || '创建失败')
|
||
}
|
||
} catch (error) {
|
||
ElMessage.error('创建分类失败')
|
||
}
|
||
}
|
||
|
||
// 图片上传与选择
|
||
const onFileSelected = async (event) => {
|
||
const file = event.target.files[0]
|
||
if (!file) return
|
||
|
||
try {
|
||
const res = await uploadFile({ file })
|
||
if (res.data.code === 200) {
|
||
form.image_ico = '/v1/attachment/get_attachment?aid=' + res.data.data.attachment_id
|
||
ElMessage.success('上传成功')
|
||
} else {
|
||
ElMessage.error('上传失败')
|
||
}
|
||
} catch (error) {
|
||
ElMessage.error('上传出错')
|
||
}
|
||
}
|
||
|
||
const openPicLibrary = () => {
|
||
picSwitch.value = true
|
||
getpicList()
|
||
}
|
||
|
||
const getpicList = async () => {
|
||
picLoading.value = true
|
||
try {
|
||
const res = await getFileList(picPagin)
|
||
if (res.data.code === 200) {
|
||
picList.value = res.data.data
|
||
total.value = res.data.count
|
||
}
|
||
} finally {
|
||
picLoading.value = false
|
||
}
|
||
}
|
||
|
||
const selectImage = (index) => {
|
||
currentIndex.value = index
|
||
}
|
||
|
||
const confirmPicSelection = () => {
|
||
if (currentIndex.value !== null) {
|
||
const item = picList.value[currentIndex.value]
|
||
form.image_ico = `/v1/attachment/get_attachment?aid=${item.attachment_id}`
|
||
picSwitch.value = false
|
||
}
|
||
}
|
||
|
||
const CurrentPageChange = (page) => {
|
||
picPagin.page = page
|
||
getpicList()
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
const submitForm = async () => {
|
||
if (!formRef.value) return
|
||
|
||
await formRef.value.validate(async (valid) => {
|
||
if (valid) {
|
||
submitting.value = true
|
||
try {
|
||
const submitData = { ...form }
|
||
|
||
// 处理分类逻辑
|
||
if (submitData.class_id) {
|
||
submitData.class_name = ''
|
||
} else if (submitData.class_name) {
|
||
submitData.class_id = ''
|
||
} else {
|
||
submitData.class_id = ''
|
||
submitData.class_name = ''
|
||
}
|
||
|
||
let res
|
||
if (isEdit.value) {
|
||
submitData.image_id = submitData.id
|
||
delete submitData.id
|
||
res = await editMirror(submitData)
|
||
} else {
|
||
res = await addVirtualMirror(submitData)
|
||
}
|
||
|
||
if (res.data.code === 200) {
|
||
ElNotification({
|
||
title: '操作成功',
|
||
message: isEdit.value ? '镜像更新成功' : '镜像创建成功',
|
||
type: 'success'
|
||
})
|
||
goBack()
|
||
} else {
|
||
ElMessage.error(res.data.msg || '操作失败')
|
||
}
|
||
} catch (error) {
|
||
console.error(error)
|
||
ElMessage.error('提交失败')
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
onMounted(() => {
|
||
initData()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.image-form-container {
|
||
max-width: 1200px;
|
||
margin: 0 auto;
|
||
padding: 24px;
|
||
}
|
||
|
||
/* 顶部导航 */
|
||
.page-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
background: #ffffff;
|
||
padding: 20px 32px;
|
||
border-radius: 12px;
|
||
border: 1px solid #e4e7ed;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.02);
|
||
}
|
||
|
||
.header-left {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.back-btn {
|
||
border: none;
|
||
background: #f2f3f5;
|
||
color: #606266;
|
||
width: 36px;
|
||
height: 36px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.back-btn:hover {
|
||
background-color: #e6e8eb;
|
||
color: #303133;
|
||
}
|
||
|
||
.header-title-area {
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
}
|
||
|
||
.page-title {
|
||
margin: 0;
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
color: #1a1a1a;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.page-subtitle {
|
||
font-size: 13px;
|
||
color: #909399;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.submit-btn {
|
||
padding: 10px 24px;
|
||
font-weight: 600;
|
||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.3);
|
||
}
|
||
|
||
/* 表单布局 */
|
||
.form-wrapper {
|
||
display: flex;
|
||
gap: 24px;
|
||
align-items: flex-start;
|
||
}
|
||
|
||
.main-form {
|
||
display: flex;
|
||
width: 100%;
|
||
gap: 24px;
|
||
}
|
||
|
||
.form-main-col {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.form-side-col {
|
||
width: 320px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 卡片样式 */
|
||
.premium-card {
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 12px;
|
||
background: #ffffff;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.premium-card:hover {
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.04);
|
||
}
|
||
|
||
.premium-card :deep(.el-card__body) {
|
||
padding: 32px;
|
||
}
|
||
|
||
/* 章节标题 */
|
||
.section-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 24px;
|
||
padding-bottom: 16px;
|
||
border-bottom: 1px solid #f0f2f5;
|
||
}
|
||
|
||
.section-header.small {
|
||
margin-bottom: 20px;
|
||
padding-bottom: 12px;
|
||
}
|
||
|
||
.section-icon {
|
||
width: 32px;
|
||
height: 32px;
|
||
border-radius: 8px;
|
||
background: linear-gradient(135deg, #ecf5ff 0%, #d9ecff 100%);
|
||
color: #409EFF;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.section-info h3 {
|
||
margin: 0;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #303133;
|
||
}
|
||
|
||
.section-info p {
|
||
margin: 2px 0 0 0;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
/* 表单网格 */
|
||
.form-grid-2 {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 24px;
|
||
}
|
||
|
||
/* 新分类输入 */
|
||
.new-category-input {
|
||
margin-top: 12px;
|
||
padding: 12px;
|
||
background: #f8f9fa;
|
||
border-radius: 8px;
|
||
border: 1px dashed #dcdfe6;
|
||
}
|
||
|
||
/* 图标上传器 */
|
||
.icon-uploader {
|
||
width: 100%;
|
||
}
|
||
|
||
.icon-preview {
|
||
position: relative;
|
||
width: 100%;
|
||
height: 160px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
border: 1px solid #e4e7ed;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f8f9fa;
|
||
}
|
||
|
||
.icon-preview img {
|
||
max-width: 100%;
|
||
max-height: 100%;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.icon-actions {
|
||
position: absolute;
|
||
top: 8px;
|
||
right: 8px;
|
||
}
|
||
|
||
.upload-area {
|
||
border: 2px dashed #e4e7ed;
|
||
border-radius: 8px;
|
||
padding: 24px;
|
||
text-align: center;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.upload-area:hover {
|
||
border-color: #409EFF;
|
||
background: #f2f6fc;
|
||
}
|
||
|
||
.upload-placeholder {
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.upload-icon {
|
||
font-size: 32px;
|
||
color: #909399;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.upload-text {
|
||
font-size: 13px;
|
||
color: #606266;
|
||
}
|
||
|
||
.upload-buttons {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
|
||
|
||
/* 素材库网格 */
|
||
.pic-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(5, 1fr);
|
||
gap: 16px;
|
||
margin: 20px 0;
|
||
max-height: 400px;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.pic-item {
|
||
position: relative;
|
||
border: 1px solid #e4e7ed;
|
||
border-radius: 8px;
|
||
padding: 12px;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.pic-item:hover {
|
||
border-color: #409EFF;
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.pic-item.active {
|
||
border-color: #409EFF;
|
||
background: #ecf5ff;
|
||
}
|
||
|
||
.pic-item img {
|
||
width: 48px;
|
||
height: 48px;
|
||
object-fit: contain;
|
||
}
|
||
|
||
.pic-name {
|
||
font-size: 12px;
|
||
color: #606266;
|
||
text-align: center;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.pic-check {
|
||
position: absolute;
|
||
top: 4px;
|
||
right: 4px;
|
||
color: #409EFF;
|
||
}
|
||
|
||
.pagination-wrapper {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
/* 响应式 */
|
||
@media screen and (max-width: 992px) {
|
||
.main-form {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.form-side-col {
|
||
width: 100%;
|
||
}
|
||
}
|
||
|
||
@media screen and (max-width: 768px) {
|
||
.image-form-container {
|
||
padding: 16px;
|
||
}
|
||
|
||
.page-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
}
|
||
|
||
.header-actions {
|
||
width: 100%;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.header-actions .el-button {
|
||
flex: 1;
|
||
}
|
||
|
||
.form-grid-2 {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.pic-grid {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
}
|
||
}
|
||
</style>
|