You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

793 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="场馆名称" prop="storeName">
<el-input
v-model="queryParams.storeName"
placeholder="请输入场馆名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input
v-model="queryParams.address"
placeholder="请输入地址"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="场馆电话" prop="phone">
<el-input
v-model="queryParams.phone"
placeholder="请输入场馆电话"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item style="float: right">
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table
v-loading="loading"
:data="storeList"
@selection-change="handleSelectionChange"
border
:header-cell-style="{'text-align': 'center'}"
:cell-style="{'text-align': 'center'}"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="场馆图片" align="center" prop="banner" width="220">
<template slot-scope="scope">
<div v-if="scope.row.banner" class="album-images">
<el-image
v-for="(img, index) in splitImages(scope.row.banner).slice(0, 3)"
:key="index"
:src="getFullImgUrl(img)"
:preview-src-list="splitImages(scope.row.banner).map(item => getFullImgUrl(item))"
class="album-img"
fit="cover"
:lazy="false"
></el-image>
<span v-if="splitImages(scope.row.banner).length > 3" class="more-count">
+{{ splitImages(scope.row.banner).length - 3 }}
</span>
</div>
<span v-else class="no-image">无图片</span>
</template>
</el-table-column>
<el-table-column label="场馆名称" align="center" prop="storeName" />
<el-table-column label="地址" align="center" prop="address" />
<el-table-column label="门店电话" align="center" prop="phone" />
<el-table-column label="创始人/店长简介" align="center" prop="founder">
<template slot-scope="scope">
<el-button
type="text"
@click="showFounderDialog(scope.row.founder)"
size="mini"
>查看内容</el-button>
</template>
</el-table-column>
<el-table-column label="企业/场馆简介" align="center" prop="profile">
<template slot-scope="scope">
<el-button
type="text"
@click="showProfileDialog(scope.row.profile)"
size="mini"
>查看内容</el-button>
</template>
</el-table-column>
<el-table-column label="对应校区" align="center" prop="deptName" />
<el-table-column label="是否展示" align="center" width="100">
<template slot-scope="scope">
<el-switch
v-model="scope.row.display"
active-value="1"
inactive-value="0"
@change="handleStatusChange(scope.row)"
></el-switch>
</template>
</el-table-column>
<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="['system:store:edit']"
>修改</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"
/>
<!-- 添加或修改门店对话框 -->
<el-dialog :title="title" :visible.sync="open" width="800px" append-to-body @opened="onDialogOpened">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="场馆图片" prop="banner">
<el-upload
:action="uploadImgUrl"
list-type="picture-card"
:file-list="albumFileList"
:on-success="handleAlbumSuccess"
:on-remove="handleAlbumRemove"
:limit="6"
:on-exceed="handleExceed"
:before-upload="beforeAlbumUpload"
:headers="uploadHeaders"
>
<i class="el-icon-plus"></i>
<div slot="tip" class="el-upload__tip">
支持JPG/PNG格式单张不超过2MB最多上传6张
</div>
</el-upload>
</el-form-item>
<el-form-item label="场馆名称" prop="storeName">
<el-input v-model="form.storeName" placeholder="请输入校区名称(展示)" />
</el-form-item>
<el-form-item label="地址" prop="address">
<el-input v-model="form.address" placeholder="请输入地址" />
</el-form-item>
<el-form-item label="场馆电话" prop="phone">
<el-input v-model="form.phone" placeholder="请输入场馆电话" />
</el-form-item>
<!-- 创始人/店长简介 - 富文本编辑器 -->
<el-form-item label="创始人/店长简介" prop="founder">
<div class="editor-container founder-editor">
<div ref="founderEditor" style="width: 100%; min-height: 200px;"></div>
</div>
</el-form-item>
<!-- 企业/门店简介 - 富文本编辑器 -->
<el-form-item label="企业/场馆简介" prop="profile">
<div class="editor-container profile-editor">
<div ref="profileEditor" style="width: 100%; min-height: 300px;"></div>
</div>
</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>
<!-- 创始人简介查看对话框 -->
<el-dialog
title="创始人/店长简介"
:visible.sync="founderDialogVisible"
width="600px"
:before-close="handleFounderDialogClose"
>
<div v-html="founderDialogContent" class="content-viewer"></div>
</el-dialog>
<!-- 门店简介查看对话框 -->
<el-dialog
title="企业/场馆简介"
:visible.sync="profileDialogVisible"
width="800px"
:before-close="handleProfileDialogClose"
>
<div v-html="profileDialogContent" class="content-viewer"></div>
</el-dialog>
</div>
</template>
<script>
import { listStore, getStore, delStore, addStore, updateStore } from "@/api/columns/store";
import WangEditor from 'wangeditor';
import { getToken } from '@/utils/auth';
import axios from 'axios';
import Pagination from '@/components/Pagination';
import RightToolbar from '@/components/RightToolbar';
export default {
name: "Store",
components: {
Pagination,
RightToolbar
},
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 门店表格数据
storeList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 创始人简介对话框
founderDialogVisible: false,
founderDialogContent: "",
// 门店简介对话框
profileDialogVisible: false,
profileDialogContent: "",
// 富文本编辑器实例
founderEditor: null,
profileEditor: null,
// 上传相关配置
uploadHeaders: {
Authorization: getToken()
},
uploadImgUrl: process.env.VUE_APP_BASE_API + "/com/file/upload",
isUploading: false,
baseUrl: process.env.VUE_APP_BASE_API || '',
albumFileList: [],
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
banner: null,
storeName: null,
address: null,
phone: null,
founder: null,
profile: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
storeName: [
{ required: true, message: "场馆名称不能为空", trigger: "blur" }
],
phone: [
{ required: true, message: "场馆电话不能为空", trigger: "blur" }
],
address: [
{ required: true, message: "场馆地址不能为空", trigger: "blur" }
]
}
};
},
created() {
this.getList();
},
methods: {
// 角色状态修改
handleStatusChange(row) {
let text = row.display === "0" ? "不展示" : "展示";
this.$modal.confirm('确认要' + text + '"' + row.storeName + '"场馆吗?').then(function() {
return updateStore({deptId:row.deptId, display:row.display});
}).then(() => {
this.$modal.msgSuccess(text + "成功");
}).catch(function() {
row.display = row.display === "0" ? "1" : "0";
});
},
/** 对话框打开后初始化富文本编辑器 */
onDialogOpened() {
if (!this.founderEditor) {
this.initFounderEditor();
}
if (!this.profileEditor) {
this.initProfileEditor();
}
},
/** 初始化创始人/店长简介富文本编辑器 */
initFounderEditor() {
if (this.$refs.founderEditor) {
this.founderEditor = new WangEditor(this.$refs.founderEditor);
// 关闭默认网络图片上传
this.founderEditor.config.showLinkImg = false;
// 自定义图片上传逻辑
this.founderEditor.config.customUploadImg = async (resultFiles, insertImgFn) => {
this.isUploading = true;
try {
for (const file of resultFiles) {
const formData = new FormData();
formData.append('file', file);
const response = await axios({
url: this.uploadImgUrl,
method: 'post',
data: formData,
headers: {
...this.uploadHeaders,
'Content-Type': 'multipart/form-data'
},
timeout: 30000
});
if (response.data && response.data.code === 200 && response.data.data) {
// 存储相对路径,显示时拼接基础路径
const relativeUrl = response.data.data;
const fullUrl = this.getFullImgUrl(relativeUrl);
// 插入图片并设置正确路径
insertImgFn(fullUrl, file.name || '图片', 'max-width:100%;height:auto;');
// 强制同步内容到表单
this.form.founder = this.founderEditor.txt.html();
this.$message.success('图片上传成功');
} else {
this.$message.error('图片上传失败: ' + (response.data?.msg || '服务器返回异常'));
}
}
} catch (error) {
console.error('创始人简介图片上传错误:', error);
this.$message.error('上传失败,请检查网络或联系管理员');
} finally {
this.isUploading = false;
}
};
// 内容变化时同步到表单
this.founderEditor.config.onchange = (html) => {
const processedHtml = html.replace(/<img/g, '<img style="max-width:100%;height:auto;"');
this.form.founder = processedHtml;
};
// 配置菜单栏(简化版)
this.founderEditor.config.menus = [
'head', 'bold', 'fontSize', 'foreColor', 'backColor',
'link', 'list', 'justify', 'quote', 'image', 'undo', 'redo'
];
// 创建编辑器实例
this.founderEditor.create();
} else {
setTimeout(() => this.initFounderEditor(), 100);
}
},
/** 初始化企业/门店简介富文本编辑器 */
initProfileEditor() {
if (this.$refs.profileEditor) {
this.profileEditor = new WangEditor(this.$refs.profileEditor);
// 关闭默认网络图片上传
this.profileEditor.config.showLinkImg = false;
// 自定义图片上传逻辑
this.profileEditor.config.customUploadImg = async (resultFiles, insertImgFn) => {
this.isUploading = true;
try {
for (const file of resultFiles) {
const formData = new FormData();
formData.append('file', file);
const response = await axios({
url: this.uploadImgUrl,
method: 'post',
data: formData,
headers: {
...this.uploadHeaders,
'Content-Type': 'multipart/form-data'
},
timeout: 30000
});
if (response.data && response.data.code === 200 && response.data.data) {
// 存储相对路径,显示时拼接基础路径
const relativeUrl = response.data.data;
const fullUrl = this.getFullImgUrl(relativeUrl);
// 插入图片并设置正确路径
insertImgFn(fullUrl, file.name || '图片', 'max-width:100%;height:auto;');
// 强制同步内容到表单
this.form.profile = this.profileEditor.txt.html();
this.$message.success('图片上传成功');
} else {
this.$message.error('图片上传失败: ' + (response.data?.msg || '服务器返回异常'));
}
}
} catch (error) {
console.error('场馆简介图片上传错误:', error);
this.$message.error('上传失败,请检查网络或联系管理员');
} finally {
this.isUploading = false;
}
};
// 内容变化时同步到表单
this.profileEditor.config.onchange = (html) => {
const processedHtml = html.replace(/<img/g, '<img style="max-width:100%;height:auto;"');
this.form.profile = processedHtml;
};
// 配置完整菜单栏
this.profileEditor.config.menus = [
'head', 'bold', 'fontSize', 'fontName', 'italic', 'underline',
'strikeThrough', 'foreColor', 'backColor', 'link', 'list',
'justify', 'quote', 'emoticon', 'image', 'table', 'undo', 'redo'
];
// 创建编辑器实例
this.profileEditor.create();
} else {
setTimeout(() => this.initProfileEditor(), 100);
}
},
/** 查询门店列表 */
getList() {
this.loading = true;
listStore(this.queryParams).then(response => {
this.storeList = response.rows || [];
this.total = response.total || 0;
this.loading = false;
}).catch(() => {
this.loading = false;
this.$message.error('获取列表数据失败');
});
},
/** 显示创始人简介对话框 */
showFounderDialog(content) {
let processedContent = content || '暂无简介';
processedContent = processedContent.replace(
/<img/g,
'<img style="max-width:100%;height:auto;margin:10px auto;display:block;"'
);
this.founderDialogContent = processedContent;
this.founderDialogVisible = true;
},
/** 关闭创始人简介对话框 */
handleFounderDialogClose() {
this.founderDialogContent = "";
this.founderDialogVisible = false;
},
/** 显示门店简介对话框 */
showProfileDialog(content) {
let processedContent = content || '暂无简介';
processedContent = processedContent.replace(
/<img/g,
'<img style="max-width:100%;height:auto;margin:10px auto;display:block;"'
);
this.profileDialogContent = processedContent;
this.profileDialogVisible = true;
},
/** 关闭门店简介对话框 */
handleProfileDialogClose() {
this.profileDialogContent = "";
this.profileDialogVisible = false;
},
/** 分割图片URL */
splitImages(urlStr) {
if (!urlStr || typeof urlStr !== 'string') return [];
return urlStr.split(',').filter(img => img && img.trim());
},
/** 处理相册上传成功 */
handleAlbumSuccess(response, file, fileList) {
if (response.code === 200) {
file.url = this.getFullImgUrl(response.data);
this.form.banner = fileList.map(f => f.url.replace(this.baseUrl, '')).join(',');
} else {
this.$message.error('上传失败:' + response.msg);
}
},
/** 相册图片删除 */
handleAlbumRemove(file, fileList) {
this.form.banner = fileList.map(f => f.url.replace(this.baseUrl, '')).join(',');
},
/** 上传数量超限 */
handleExceed(files, fileList) {
this.$message.warning(`最多只能上传6张图片`);
},
/** 相册上传前校验 */
beforeAlbumUpload(file) {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('只能上传JPG/PNG格式的图片');
return false;
}
if (!isLt2M) {
this.$message.error('图片大小不能超过2MB');
return false;
}
return true;
},
/** 处理图片URL */
getFullImgUrl(url) {
if (!url || typeof url !== 'string') return '';
if (url.startsWith('http://') || url.startsWith('https://')) {
return url;
}
return this.baseUrl + (this.baseUrl.endsWith('/') ? '' : '/') + url;
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
banner: null,
storeName: null,
address: null,
phone: null,
founder: null,
profile: null,
deptId: null
};
this.resetForm("form");
// 清空富文本编辑器内容
if (this.founderEditor) {
this.founderEditor.txt.clear();
}
if (this.profileEditor) {
this.profileEditor.txt.clear();
}
this.albumFileList = [];
this.isUploading = false;
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.deptId)
this.single = selection.length !== 1
this.multiple = !selection.length
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加场馆";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const deptId = row?.deptId || (this.ids.length > 0 ? this.ids[0] : null);
if (!deptId) {
this.$message.warning('请选择一条记录进行修改');
return;
}
getStore(deptId).then(response => {
this.form = { ...response.data };
// 富文本回显 - 创始人简介
this.$nextTick(() => {
setTimeout(() => {
if (this.founderEditor && this.form.founder) {
const processedHtml = this.form.founder.replace(
/<img/g,
'<img style="max-width:100%;height:auto;"'
);
this.founderEditor.txt.html(processedHtml);
}
}, 500);
});
// 富文本回显 - 门店简介
this.$nextTick(() => {
setTimeout(() => {
if (this.profileEditor && this.form.profile) {
const processedHtml = this.form.profile.replace(
/<img/g,
'<img style="max-width:100%;height:auto;"'
);
this.profileEditor.txt.html(processedHtml);
}
}, 500);
});
// 门店图片回显
if (this.form.banner) {
this.albumFileList = this.splitImages(this.form.banner).map(url => ({
url: this.getFullImgUrl(url),
name: 'store-img',
status: 'success'
}));
}
this.open = true;
this.title = "修改场馆";
}).catch(() => {
this.$message.error('获取数据失败');
});
},
/** 提交按钮 */
submitForm() {
// 强制同步富文本内容并校验
if (this.founderEditor) {
const founderHtml = this.founderEditor.txt.html();
// 检查是否存在空图片路径
if (founderHtml.includes('<img') && founderHtml.includes('src=""')) {
this.$message.error('创始人简介中存在无效图片,请重新上传');
return;
}
this.form.founder = founderHtml;
}
if (this.profileEditor) {
const profileHtml = this.profileEditor.txt.html();
// 检查是否存在空图片路径
if (profileHtml.includes('<img') && profileHtml.includes('src=""')) {
this.$message.error('场馆简介中存在无效图片,请重新上传');
return;
}
this.form.profile = profileHtml;
}
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.deptId != null) {
updateStore(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
}).catch(() => {
this.$modal.msgError("修改失败");
});
} else {
addStore(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
}).catch(() => {
this.$modal.msgError("新增失败");
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const deptIds = row?.deptId || this.ids;
if (!deptIds || (Array.isArray(deptIds) && deptIds.length === 0)) {
this.$message.warning('请选择至少一条记录进行删除');
return;
}
this.$modal.confirm(`是否确认删除选中的场馆信息?`).then(() => {
return delStore(deptIds);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 导出按钮操作 */
handleExport() {
this.download('system/store/export', {
...this.queryParams
}, `store_${new Date().getTime()}.xlsx`)
}
},
beforeDestroy() {
// 销毁富文本编辑器实例
if (this.founderEditor) {
this.founderEditor.destroy();
this.founderEditor = null;
}
if (this.profileEditor) {
this.profileEditor.destroy();
this.profileEditor = null;
}
}
};
</script>
<style scoped>
/* 相册样式 */
.album-images {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.album-img {
width: 60px;
height: 60px;
border-radius: 4px;
cursor: pointer;
}
.more-count {
width: 60px;
height: 60px;
background: #f5f5f5;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
color: #666;
border: 1px solid #eee;
}
.no-image {
color: #999;
font-size: 14px;
}
/* 富文本样式 */
.editor-container {
border: 1px solid #e6e6e6;
border-radius: 4px;
padding: 5px;
margin-bottom: 10px;
}
.founder-editor {
min-height: 200px;
}
.profile-editor {
min-height: 300px;
}
.editor-container img {
max-width: 100% !important;
height: auto !important;
}
/* 查看内容样式 */
.content-viewer {
line-height: 1.8;
word-break: break-all;
padding: 15px;
max-height: 600px;
overflow-y: auto;
}
.content-viewer img {
max-width: 100% !important;
height: auto !important;
margin: 10px auto !important;
display: block !important;
}
</style>