ai_images/admin-ui/src/views/ai/template/index.vue

469 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['ai:template:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['ai:template:edit']"
>修改</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['ai:template:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['ai:template:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="templateList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="AI类型" align="center" prop="aiId">
<template slot-scope="scope">
<span>{{ getAiTypeLabel(scope.row.aiId) }}</span>
</template>
</el-table-column>
<el-table-column label="模版中文" align="center" prop="chineseContent" />
<el-table-column label="模版英文" align="center" prop="englishContent" />
<el-table-column label="模版图片URL" align="center" prop="imageUrl">
<template slot-scope="scope">
<span v-if="scope.row.imageUrl" style="margin-right: 10px;">{{ scope.row.imageUrl }}</span>
<el-button
v-if="scope.row.imageUrl"
size="mini"
type="text"
icon="el-icon-download"
@click="handleDownloadImage(scope.row.imageUrl)"
>保存</el-button>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template slot-scope="scope">
<span v-if="scope.row.status === 0">停用</span>
<span v-else-if="scope.row.status === 1">正常</span>
<span v-else>{{ scope.row.status }}</span>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['ai:template:edit']"
>修改</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['ai:template:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改AI模版对话框 -->
<el-dialog :title="title" :visible.sync="open" width="75%" :close-on-click-modal="false" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="AI类型" prop="aiId">
<el-select v-model="form.aiId" placeholder="请选择AI类型" clearable>
<el-option
v-for="item in aiTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="模版中文">
<el-input v-model="form.chineseContent" placeholder="请输入模版中文"/>
</el-form-item>
<el-form-item label="模版英文">
<el-input v-model="form.englishContent" placeholder="请输入模版英文"/>
</el-form-item>
<el-form-item label="模版图片" prop="imageUrl">
<div style="display: flex; align-items: center; gap: 12px;">
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false"
:http-request="handleUpload"
>
<img v-if="form.imageUrl" :src="form.imageUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
<span style="color: #909399; font-size: 12px;">支持jpg、png格式建议尺寸800 * 400</span>
</div>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio :label="0">停用</el-radio>
<el-radio :label="1">正常</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listTemplate, getTemplate, delTemplate, addTemplate, updateTemplate } from "@/api/ai/template"
import { uploadFile } from "@/api/file/file"
import axios from 'axios'
export default {
name: "Template",
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// AI模版表格数据
templateList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
name: null,
chineseContent: null,
englishContent: null,
imageUrl: null,
aiId: null,
status: null,
},
// 表单参数
form: {},
// AI类型选项
aiTypeOptions: [
{ value: 1, label: "快捷生图" },
{ value: 11, label: "一键脱衣" },
{ value: 12, label: "图生图2" },
{ value: 13, label: "一键换脸" },
{ value: 21, label: "快捷生视频" },
{ value: 22, label: "视频换脸" }
],
// 表单校验
rules: {
chineseContent: [
{ required: true, message: "模版中文不能为空", trigger: "blur" },
],
englishContent: [
{ required: true, message: "模版英文不能为空", trigger: "blur" },
],
imageUrl: [
{ required: true, message: "模版图片不能为空", trigger: "change" },
],
status: [
{ required: true, message: "状态不能为空", trigger: "blur" },
]
}
}
},
created() {
this.getList()
},
methods: {
/** 查询AI模版列表 */
getList() {
this.loading = true
listTemplate(this.queryParams).then(response => {
this.templateList = response.rows
this.total = response.total
this.loading = false
})
},
// 取消按钮
cancel() {
this.open = false
this.reset()
},
// 表单重置
reset() {
this.form = {
id: null,
name: null,
chineseContent: null,
englishContent: null,
imageUrl: null,
aiId: null,
status: 1, // 默认状态为正常
remark: null,
createTime: null,
createBy: null,
updateBy: null,
updateTime: null,
delFlag: null
}
this.resetForm("form")
},
/** 获取AI类型标签 */
getAiTypeLabel(aiId) {
if (aiId == null) return '-'
// 处理数字或字符串类型的 aiId
const value = typeof aiId === 'string' ? parseInt(aiId) : aiId
const option = this.aiTypeOptions.find(item => item.value === value)
return option ? option.label : aiId || '-'
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1
this.getList()
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm")
this.handleQuery()
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.id)
this.single = selection.length!==1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset()
this.open = true
this.title = "添加AI模版"
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset()
const id = row.id || this.ids
getTemplate(id).then(response => {
this.form = response.data
this.open = true
this.title = "修改AI模版"
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id != null) {
updateTemplate(this.form).then(response => {
this.$modal.msgSuccess("修改成功")
this.open = false
this.getList()
})
} else {
addTemplate(this.form).then(response => {
this.$modal.msgSuccess("新增成功")
this.open = false
this.getList()
})
}
}
})
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id || this.ids
this.$modal.confirm('是否确认删除AI模版编号为"' + ids + '"的数据项?').then(function() {
return delTemplate(ids)
}).then(() => {
this.getList()
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 导出按钮操作 */
handleExport() {
this.download('ai/template/export', {
...this.queryParams
}, `template_${new Date().getTime()}.xlsx`)
},
/** 处理图片上传 */
handleUpload(params) {
const { file } = params; // 获取上传的文件对象
let formData = new FormData();
formData.append("file", file);
this.$modal.loading("正在上传图片,请稍候...");
uploadFile(formData).then(response => {
this.form.imageUrl = response.url; // 获取S3 CDN URL
this.$modal.closeLoading();
this.$modal.msgSuccess("图片上传成功");
// 触发表单验证
this.$refs["form"].validateField("imageUrl");
}).catch(error => {
this.$modal.closeLoading();
this.$modal.msgError("图片上传失败,请重试");
console.error('上传图片失败:', error);
});
},
/** 下载图片到本地支持AWS CDN */
async handleDownloadImage(imageUrl) {
if (!imageUrl) {
this.$modal.msgWarning("图片URL为空")
return
}
try {
this.$modal.loading("正在下载图片,请稍候...")
// 方法1: 使用fetch获取AWS CDN图片支持CORS
const response = await fetch(imageUrl, {
method: 'GET',
mode: 'cors',
cache: 'no-cache'
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const blob = await response.blob()
// 从URL中提取文件名处理AWS CDN URL
const urlParts = imageUrl.split('/')
let filename = urlParts[urlParts.length - 1].split('?')[0] // 移除查询参数
// 如果文件名不包含扩展名尝试从Content-Type获取
if (!filename.includes('.')) {
const contentType = response.headers.get('content-type') || 'image/jpeg'
const extension = contentType.split('/')[1]?.split(';')[0] || 'jpg'
// 从URL路径中尝试提取扩展名
const pathMatch = imageUrl.match(/\.(jpg|jpeg|png|gif|bmp|webp|svg)/i)
if (pathMatch) {
filename = `image_${new Date().getTime()}.${pathMatch[1]}`
} else {
filename = `image_${new Date().getTime()}.${extension}`
}
}
// 使用file-saver保存文件
this.$download.saveAs(blob, filename)
this.$modal.closeLoading()
this.$modal.msgSuccess("图片下载成功")
} catch (error) {
this.$modal.closeLoading()
console.error('下载图片失败:', error)
// 方法2: 如果fetch失败尝试使用axios可能需要后端代理
try {
const response = await axios({
method: 'get',
url: imageUrl,
responseType: 'blob',
timeout: 30000,
headers: {
'Accept': 'image/*'
}
})
const blob = new Blob([response.data])
const urlParts = imageUrl.split('/')
let filename = urlParts[urlParts.length - 1].split('?')[0]
if (!filename.includes('.')) {
const contentType = response.headers['content-type'] || 'image/jpeg'
const extension = contentType.split('/')[1]?.split(';')[0] || 'jpg'
filename = `image_${new Date().getTime()}.${extension}`
}
this.$download.saveAs(blob, filename)
this.$modal.msgSuccess("图片下载成功")
} catch (axiosError) {
console.error('axios下载也失败:', axiosError)
this.$modal.msgError("图片下载失败可能是跨域限制。请检查AWS CDN的CORS配置")
}
}
}
}
}
</script>
<style scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
object-fit: contain;
}
</style>