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.

850 lines
26 KiB

6 months ago
<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="banner">-->
<!-- <el-input-->
<!-- v-model="queryParams.banner"-->
<!-- placeholder="请输入门店图片标识"-->
<!-- clearable-->
<!-- @keyup.enter.native="handleQuery"-->
<!-- />-->
<!-- </el-form-item>-->
<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>
2 months ago
<el-form-item style="float: right">
6 months ago
<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">
<!-- <el-col :span="1.5">-->
<!-- <el-button-->
<!-- type="primary"-->
<!-- plain-->
<!-- icon="el-icon-plus"-->
<!-- size="mini"-->
<!-- @click="handleAdd"-->
<!-- v-hasPermi="['system:store: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="['system:store: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="['system:store: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="['system:store:export']"-->
<!-- >导出</el-button>-->
<!-- </el-col>-->
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
2 months ago
<el-table
v-loading="loading"
:data="storeList"
@selection-change="handleSelectionChange"
border
:header-cell-style="{'text-align': 'center'}"
:cell-style="{'text-align': 'center'}"
>
6 months ago
<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>
2 months ago
<el-table-column label="所属校区" align="center" prop="deptName" />
4 months ago
<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>
6 months ago
<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>
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- icon="el-icon-delete"-->
<!-- @click="handleDelete(scope.row)"-->
<!-- v-hasPermi="['system:store: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"
/>
<!-- 添加或修改门店对话框 -->
<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: {
4 months ago
// 角色状态修改
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";
});
},
6 months ago
/** 对话框打开后初始化富文本编辑器 */
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>