2873 lines
97 KiB
Vue
2873 lines
97 KiB
Vue
<template>
|
||
<div class="sysseting-container">
|
||
<el-tabs v-model="activeTab">
|
||
<el-tab-pane label="基本信息" name="base">
|
||
<el-form :model="baseForm" label-width="120px" class="tab-form">
|
||
<el-form-item label="投诉电话">
|
||
<el-input v-model="baseForm.phone" placeholder="请输入投诉电话" style="width: 300px" />
|
||
</el-form-item>
|
||
<el-form-item label="抢单开始时间">
|
||
<el-time-picker v-model="baseForm.startTime" placeholder="选择时间" format="HH:mm" value-format="HH:mm" style="width: 150px" />
|
||
</el-form-item>
|
||
<el-form-item label="抢单结束时间">
|
||
<el-time-picker v-model="baseForm.endTime" placeholder="选择时间" format="HH:mm" value-format="HH:mm" style="width: 150px" />
|
||
</el-form-item>
|
||
<el-form-item label="秒杀结束时间">
|
||
<el-date-picker
|
||
v-model="baseForm.mstime"
|
||
type="datetime"
|
||
placeholder="选择秒杀结束时间"
|
||
format="yyyy-MM-dd HH:mm:ss"
|
||
value-format="yyyy-MM-dd HH:mm:ss"
|
||
style="width: 300px"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="质保金扣除比例">
|
||
<el-input-number v-model="baseForm.marginRate" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
</el-form-item>
|
||
<el-form-item label="消费金">
|
||
<el-input-number v-model="baseForm.consumption" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="用户支付服务费用后根据用户的实际支付换算得到的消费金比例" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="服务金">
|
||
<el-input-number v-model="baseForm.servicefee" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="用户支付商品费用后根据用户的实际支付换算得到的服务金比例" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="消费金抵扣比例">
|
||
<el-input-number v-model="baseForm.consumption_deduction" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="用户支付订单时可用消费金抵扣的最大比例" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="服务金抵扣比例">
|
||
<el-input-number v-model="baseForm.service_deduction" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="用户支付订单时可用服务金抵扣的最大比例" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="物料分佣">
|
||
<el-input-number v-model="baseForm.material_commissions" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="平台统一设置,师傅使用物料后师傅所得物料分佣的比例" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="会员优惠">
|
||
<el-input-number v-model="baseForm.member_discount" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="包年会员在平台消费进行的折扣" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="充值附送比例">
|
||
<el-input-number v-model="baseForm.recharge_discount" :min="0" :max="100" />
|
||
<span style="margin-left: 8px">%</span>
|
||
<el-tooltip content="用户充值时按照用户充值金额的比例进行赠送" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="下单送积分">
|
||
<el-input v-model="baseForm.orderScore" style="width: 120px" />
|
||
<el-tooltip content="下单支付金额多少元赠送1积分,不填则不赠送" placement="right">
|
||
<i class="el-icon-question" style="margin-left: 8px; color: #999" />
|
||
</el-tooltip>
|
||
</el-form-item>
|
||
<el-form-item label="搜索热词">
|
||
<el-select v-model="baseForm.hotwords" multiple filterable allow-create default-first-option placeholder="请输入热词" style="width: 600px">
|
||
<el-option v-for="item in baseForm.hotwords" :key="item" :label="item" :value="item" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="客服二维码">
|
||
<el-upload
|
||
class="avatar-uploader"
|
||
action="#"
|
||
:show-file-list="false"
|
||
:on-change="handleQrChange"
|
||
:before-upload="beforeQrUpload"
|
||
>
|
||
<img v-if="baseForm.qrUrl" :src="baseForm.qrUrl" class="qr-img" />
|
||
<i v-else class="el-icon-plus avatar-uploader-icon" />
|
||
</el-upload>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('1')">提交</el-button>
|
||
<el-button @click="resetBase">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="文本配置" name="text">
|
||
<el-form :model="textForm" label-width="120px" class="tab-form">
|
||
<el-form-item label="公司名称">
|
||
<el-input v-model="textForm.company" placeholder="请输入公司名称" />
|
||
</el-form-item>
|
||
<el-form-item label="简介">
|
||
<el-input type="textarea" v-model="textForm.intro" :rows="4" placeholder="请输入公司简介" />
|
||
</el-form-item>
|
||
<el-form-item label="质保金说明">
|
||
<Editor v-model="textForm.marginDesc" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('2')">提交</el-button>
|
||
<el-button @click="resetText">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="下单时间配置" name="orderTime">
|
||
<el-form label-width="120px" class="tab-form">
|
||
<el-form-item label="下单时间">
|
||
<div v-for="(item, idx) in orderTimes" :key="idx" style="display: flex; align-items: center; margin-bottom: 8px;">
|
||
<el-time-picker
|
||
v-model="item.start"
|
||
placeholder="开始时间"
|
||
format="HH:mm"
|
||
value-format="HH:mm"
|
||
style="width: 130px; margin-right: 8px;"
|
||
@change="updateTimeRange(idx)"
|
||
/>
|
||
<span style="margin: 0 4px;">-</span>
|
||
<el-time-picker
|
||
v-model="item.end"
|
||
placeholder="结束时间"
|
||
format="HH:mm"
|
||
value-format="HH:mm"
|
||
style="width: 130px; margin-right: 12px;"
|
||
@change="updateTimeRange(idx)"
|
||
/>
|
||
<el-input-number v-model="item.count" :min="0" style="width:25%; margin-right: 12px;" />
|
||
<el-button icon="el-icon-delete" type="danger" @click="removeOrderTime(idx)" circle />
|
||
</div>
|
||
<el-button type="primary" icon="el-icon-plus" @click="addOrderTime">新增</el-button>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('3')">提交</el-button>
|
||
<el-button @click="resetOrderTime">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="时间配置" name="time">
|
||
<el-form :model="timeForm" label-width="120px" class="tab-form">
|
||
<el-form-item label="每月提现时间">
|
||
<el-select v-model="timeForm.withdrawDays" multiple placeholder="请选择日期" style="width: 300px">
|
||
<el-option v-for="d in 31" :key="d" :label="d + '号'" :value="d + '号'" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="定时接单时长">
|
||
<el-input-number v-model="timeForm.autoOrderMinutes" :min="1" style="width: 120px" />
|
||
<span style="margin-left: 8px">分钟</span>
|
||
<div class="el-form-item__tip">订单无人接单时多少分钟后,自动重新派单。单位(分钟)</div>
|
||
</el-form-item>
|
||
<el-form-item label="取消订单时长">
|
||
<el-input-number v-model="timeForm.cancelOrderDays" :min="1" style="width: 120px" />
|
||
<span style="margin-left: 8px">天</span>
|
||
<div class="el-form-item__tip">师傅到达之后,多久之后没有报工则取消订单。单位(天)</div>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('4')">提交</el-button>
|
||
<el-button @click="resetTime">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="会员配置" name="config_five">
|
||
<el-form :model="memberForm" label-width="120px" class="tab-form">
|
||
<el-form-item label="会员权益说明">
|
||
<Editor v-model="memberForm.member" />
|
||
</el-form-item>
|
||
<el-form-item label="会员规则说明">
|
||
<Editor v-model="memberForm.memberRule" />
|
||
</el-form-item>
|
||
<el-form-item label="充值规则说明">
|
||
<Editor v-model="memberForm.recharge" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('5')">提交</el-button>
|
||
<el-button @click="resetMember">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="评价标签" name="config_six">
|
||
<el-form :model="ratingForm" label-width="120px" class="tab-form">
|
||
<el-form-item label="服务评价标签">
|
||
<el-select v-model="ratingForm.service" multiple filterable allow-create default-first-option placeholder="请输入服务评价标签" style="width: 600px">
|
||
<el-option v-for="item in ratingForm.service" :key="item" :label="item" :value="item" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="商品评价标签">
|
||
<el-select v-model="ratingForm.goods" multiple filterable allow-create default-first-option placeholder="请输入商品评价标签" style="width: 600px">
|
||
<el-option v-for="item in ratingForm.goods" :key="item" :label="item" :value="item" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('6')">提交</el-button>
|
||
<el-button @click="resetRating">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="通知公告" name="config_banner">
|
||
<div class="notice-container">
|
||
<!-- 页面头部操作区 -->
|
||
<div class="notice-header">
|
||
<div class="notice-title">
|
||
<h3>通知公告管理</h3>
|
||
<p class="notice-desc">管理首页展示的通知公告,支持富文本编辑和状态控制</p>
|
||
</div>
|
||
<div class="notice-header-actions">
|
||
<div class="notice-filter">
|
||
<el-select v-model="noticeFilter" placeholder="筛选分类" clearable size="small" style="width: 150px; margin-right: 12px;">
|
||
<el-option label="全部公告" value=""></el-option>
|
||
<el-option label="首页通知公告" value="home"></el-option>
|
||
<el-option label="活动通知公告" value="activity"></el-option>
|
||
</el-select>
|
||
</div>
|
||
<el-button type="primary" icon="el-icon-plus" @click="addNotice">添加公告</el-button>
|
||
<el-button type="success" icon="el-icon-check" @click="saveAllConfig('notice')">保存全部</el-button>
|
||
<el-button icon="el-icon-refresh" @click="resetNotice">重置</el-button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 公告列表 -->
|
||
<div class="notice-list">
|
||
<div v-if="filteredNoticeList.length === 0" class="notice-empty">
|
||
<el-empty description="暂无公告数据">
|
||
<el-button type="primary" @click="addNotice">添加第一条公告</el-button>
|
||
</el-empty>
|
||
</div>
|
||
|
||
<div v-for="(item, index) in filteredNoticeList" :key="index" class="notice-item">
|
||
<el-card shadow="hover" :class="['notice-card', { 'notice-offline': item.status === 0, 'notice-expanded': item.expanded }]">
|
||
|
||
<!-- 简洁行显示 -->
|
||
<div class="notice-simple-row" @click="toggleNoticeExpand(item.originalIndex)">
|
||
<div class="notice-simple-left">
|
||
<span class="notice-index">{{ item.originalIndex + 1 }}</span>
|
||
<el-tag
|
||
:type="getCategoryTagType(item.category)"
|
||
size="mini"
|
||
class="notice-category-tag-left">
|
||
{{ getCategoryName(item.category) }}
|
||
</el-tag>
|
||
<div class="notice-simple-info">
|
||
<span class="notice-simple-title">{{ item.title || '未设置标题' }}</span>
|
||
<span class="notice-simple-meta">排序: {{ item.sort || 0 }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="notice-simple-right">
|
||
<el-tag :type="noticeForm.notice[item.originalIndex].status === 1 ? 'success' : 'danger'" size="small">
|
||
{{ noticeForm.notice[item.originalIndex].status === 1 ? '已上线' : '已下线' }}
|
||
</el-tag>
|
||
<el-switch
|
||
v-model="noticeForm.notice[item.originalIndex].status"
|
||
:active-value="1"
|
||
:inactive-value="0"
|
||
size="mini"
|
||
active-color="#67C23A"
|
||
inactive-color="#F56C6C"
|
||
@change="handleStatusChange(item.originalIndex)"
|
||
@click.native.stop
|
||
style="margin: 0 12px;">
|
||
</el-switch>
|
||
<el-button
|
||
type="danger"
|
||
icon="el-icon-delete"
|
||
size="mini"
|
||
circle
|
||
@click.stop="removeNotice(item.originalIndex)">
|
||
</el-button>
|
||
<i
|
||
:class="item.expanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
|
||
class="notice-expand-icon">
|
||
</i>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 展开的编辑内容 -->
|
||
<div v-show="item.expanded" class="notice-card-content">
|
||
<el-divider style="margin: 16px 0;"></el-divider>
|
||
<el-form label-width="80px" label-position="top">
|
||
<div class="notice-form-grid">
|
||
<!-- 第一行:标题 -->
|
||
<div class="notice-form-row full-width">
|
||
<el-row :gutter="20">
|
||
<el-col :span="6">
|
||
<el-form-item label="公告标题" class="notice-title-item">
|
||
<el-input
|
||
v-model="item.title"
|
||
placeholder="请输入公告标题"
|
||
maxlength="100"
|
||
show-word-limit
|
||
clearable>
|
||
</el-input>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="公告分类" class="notice-category-item">
|
||
<el-select
|
||
v-model="item.category"
|
||
placeholder="请选择公告分类"
|
||
@change="val => handleCategoryChange(val, item, item.originalIndex)">
|
||
<el-option label="首页通知公告" value="home">
|
||
<span style="display: flex; align-items: center;">
|
||
<i class="el-icon-house" style="color: #409EFF; margin-right: 8px;"></i>
|
||
首页通知公告
|
||
</span>
|
||
</el-option>
|
||
<el-option label="活动通知公告" value="activity">
|
||
<span style="display: flex; align-items: center;">
|
||
<i class="el-icon-star-on" style="color: #E6A23C; margin-right: 8px;"></i>
|
||
活动通知公告
|
||
</span>
|
||
</el-option>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="排序" class="notice-sort-item">
|
||
<el-input-number
|
||
v-model="item.sort"
|
||
:min="0"
|
||
:max="999"
|
||
controls-position="right"
|
||
placeholder="排序值">
|
||
</el-input-number>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="跳转链接" class="notice-link-item">
|
||
<el-select
|
||
v-model="item.link"
|
||
placeholder="请选择站内地址"
|
||
filterable
|
||
clearable
|
||
style="width: 100%;">
|
||
<el-option
|
||
v-for="(link, idx) in siteLinks"
|
||
:key="idx"
|
||
:label="link.name + (link.remark ? '(' + link.remark + ')' : '')"
|
||
:value="link.url"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
|
||
<!-- 第二行:内容 -->
|
||
<div class="notice-form-row full-width">
|
||
<el-form-item label="公告内容" class="notice-content-item">
|
||
<Editor v-model="item.content" />
|
||
</el-form-item>
|
||
</div>
|
||
</div>
|
||
</el-form>
|
||
|
||
<!-- 编辑区域底部操作按钮 -->
|
||
<div class="notice-edit-actions">
|
||
<el-button type="primary" size="small" @click="saveNoticeItem(item.originalIndex)">
|
||
<i class="el-icon-check"></i> 保存
|
||
</el-button>
|
||
<el-button size="small" @click="toggleNoticeExpand(item.originalIndex)">
|
||
<i class="el-icon-close"></i> 收起
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 底部操作区 -->
|
||
<div class="notice-footer" v-if="noticeForm.notice.length > 0">
|
||
<el-divider></el-divider>
|
||
<div class="notice-footer-actions">
|
||
<el-button type="primary" icon="el-icon-plus" @click="addNotice">添加公告</el-button>
|
||
<el-button type="success" icon="el-icon-check" @click="saveAllConfig('notice')">保存全部</el-button>
|
||
<el-button icon="el-icon-refresh" @click="resetNotice">重置</el-button>
|
||
</div>
|
||
<div class="notice-footer-tips">
|
||
<el-alert
|
||
title="温馨提示"
|
||
type="info"
|
||
:closable="false"
|
||
show-icon>
|
||
<div slot="description">
|
||
<p>• 只有状态为"上线"的公告才会在前端显示</p>
|
||
<p>• 公告按排序值升序排列,数值越小越靠前</p>
|
||
<p>• 保存后需要前端重新请求接口才能看到最新内容</p>
|
||
</div>
|
||
</el-alert>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="活动专区维护" name="activity_zone">
|
||
<div class="activity-zone-container">
|
||
<div class="activity-header">
|
||
<el-button type="primary" icon="el-icon-plus" @click="addActivity">添加活动</el-button>
|
||
<el-button type="success" icon="el-icon-check" @click="saveAllConfig('activity')">保存全部</el-button>
|
||
<el-button icon="el-icon-refresh" @click="resetActivity">重置</el-button>
|
||
</div>
|
||
<div v-if="activityForm.activities.length === 0" class="activity-empty">
|
||
<el-empty description="暂无活动数据">
|
||
<el-button type="primary" @click="addActivity">添加第一条活动</el-button>
|
||
</el-empty>
|
||
</div>
|
||
<div v-for="(item, index) in activityForm.activities" :key="index" class="activity-item">
|
||
<el-card shadow="hover" :class="['activity-card', { 'activity-offline': item.status === 0, 'activity-expanded': item.expanded }]">
|
||
<!-- 简洁行显示 -->
|
||
<div class="activity-simple-row" @click="toggleActivityExpand(index)">
|
||
<div class="activity-simple-left">
|
||
<span class="activity-index">{{ index + 1 }}</span>
|
||
<div class="activity-simple-info">
|
||
<span class="activity-simple-title">{{ item.title || '未设置标题' }}</span>
|
||
<span class="activity-simple-content">{{ item.content ? (item.content.length > 20 ? item.content.slice(0, 20) + '...' : item.content) : '无内容' }}</span>
|
||
<span v-if="item.link" class="activity-simple-link">链接: {{ item.link.length > 30 ? item.link.slice(0, 30) + '...' : item.link }}</span>
|
||
<span class="activity-simple-position">显示位置: {{ item.position || 0 }}</span>
|
||
</div>
|
||
<img v-if="item.imgUrl" :src="item.imgUrl" class="activity-thumb" />
|
||
</div>
|
||
<div class="activity-simple-right">
|
||
<el-tag :type="item.status === 1 ? 'success' : 'danger'" size="small">
|
||
{{ item.status === 1 ? '已上线' : '已下线' }}
|
||
</el-tag>
|
||
<el-switch
|
||
v-model="item.status"
|
||
:active-value="1"
|
||
:inactive-value="0"
|
||
size="mini"
|
||
active-color="#67C23A"
|
||
inactive-color="#F56C6C"
|
||
@change="handleActivityStatusChange(index)"
|
||
@click.native.stop
|
||
style="margin: 0 12px;">
|
||
</el-switch>
|
||
<el-button
|
||
type="danger"
|
||
icon="el-icon-delete"
|
||
size="mini"
|
||
circle
|
||
@click.stop="removeActivity(index)">
|
||
</el-button>
|
||
<i
|
||
:class="item.expanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
|
||
class="activity-expand-icon">
|
||
</i>
|
||
</div>
|
||
</div>
|
||
<!-- 展开编辑内容 -->
|
||
<div v-show="item.expanded" class="activity-card-content">
|
||
<el-divider style="margin: 16px 0;"></el-divider>
|
||
<el-form label-width="80px" label-position="top">
|
||
<el-row :gutter="20">
|
||
<el-col :span="6">
|
||
<el-form-item label="活动标题">
|
||
<el-input v-model="item.title" placeholder="请输入活动标题" maxlength="100" show-word-limit clearable />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="活动链接">
|
||
|
||
<selecturl v-model="item.link"></selecturl>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="显示位置">
|
||
<el-input-number
|
||
v-model="item.position"
|
||
:min="0"
|
||
:max="999"
|
||
controls-position="right"
|
||
placeholder="显示位置(数字越小越靠前)"
|
||
style="width: 100%;">
|
||
</el-input-number>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="状态">
|
||
<el-switch v-model="item.status" :active-value="1" :inactive-value="0" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-form-item label="活动图片">
|
||
<el-upload
|
||
:ref="'activityUpload' + index"
|
||
:action="uploadFileUrl"
|
||
:headers="uploadHeaders"
|
||
:show-file-list="false"
|
||
:on-success="(res, file) => handleActivityImgSuccess(res, file, index)"
|
||
:on-error="(err, file) => handleActivityImgError(err, file, index)"
|
||
:before-upload="file => beforeActivityImgUpload(file, index)"
|
||
:key="'upload_' + index + '_' + (item.imgUrl ? 'has' : 'no')"
|
||
class="activity-img-uploader"
|
||
name="file">
|
||
<div style="position:relative;display:inline-block;">
|
||
<img v-if="item.imgUrl" :src="item.imgUrl" class="activity-img" style="cursor:pointer" title="点击替换图片" />
|
||
<el-button v-if="item.imgUrl" class="activity-img-delete-btn" type="danger" icon="el-icon-close" circle size="mini" @click.stop="removeActivityImg(index)" style="position:absolute;top:2px;right:2px;padding:0;width:22px;height:22px;" title="删除图片"></el-button>
|
||
<i v-else class="el-icon-plus activity-uploader-icon" style="cursor:pointer" title="点击上传图片" />
|
||
</div>
|
||
</el-upload>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-form-item label="活动内容">
|
||
<el-input v-model="item.content" type="textarea" :rows="4" placeholder="请输入活动内容" />
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<div class="activity-edit-actions">
|
||
<el-button type="primary" size="small" @click="saveActivityItem(index)">
|
||
<i class="el-icon-check"></i> 保存
|
||
</el-button>
|
||
<el-button size="small" @click="toggleActivityExpand(index)">
|
||
<i class="el-icon-close"></i> 收起
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="站内地址" name="siteLinks">
|
||
<el-form label-width="100px" class="tab-form">
|
||
<div v-for="(item, idx) in siteLinks" :key="idx" style="display: flex; align-items: center; margin-bottom: 8px;">
|
||
<el-input v-model="item.name" placeholder="名称" style="width: 120px; margin-right: 8px;" />
|
||
<el-input v-model="item.url" placeholder="地址" style="width: 220px; margin-right: 8px;" />
|
||
<!-- <el-select v-model="item.params" placeholder="选择类型">-->
|
||
<!-- <el-option value="1" label="普通链接"></el-option>-->
|
||
<!-- <el-option value="2" label="服务链接"></el-option>-->
|
||
<!-- <el-option value="3" label="商品链接"></el-option>-->
|
||
<!-- </el-select>-->
|
||
<!-- <el-input v-model="item.params" placeholder="参数" style="width: 160px; margin-right: 8px;" />-->
|
||
<el-input v-model="item.remark" placeholder="备注" style="width: 160px; margin-right: 8px;" />
|
||
|
||
<el-button icon="el-icon-delete" type="danger" @click="removeSiteLink(idx)" circle />
|
||
</div>
|
||
<el-button type="primary" icon="el-icon-plus" @click="addSiteLink">新增</el-button>
|
||
<el-button type="success" @click="saveSiteLinks" style="margin-left: 12px;">保存</el-button>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="上门标准" name="serviceStandard">
|
||
<el-form :model="serviceStandardForm" label-width="120px" class="tab-form">
|
||
<el-form-item label="上门标准说明">
|
||
<Editor v-model="serviceStandardForm.serviceStandard" />
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="saveAllConfig('serviceStandard')">提交</el-button>
|
||
<el-button @click="resetServiceStandard">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
</el-tab-pane>
|
||
<el-tab-pane label="首页弹窗维护" name="popupConfig">
|
||
<div class="popup-container">
|
||
<div class="popup-header">
|
||
<el-button type="primary" icon="el-icon-plus" @click="addPopup">添加弹窗</el-button>
|
||
<el-button type="success" icon="el-icon-check" @click="saveAllConfig('popup')">保存全部</el-button>
|
||
<el-button icon="el-icon-refresh" @click="resetPopup">重置</el-button>
|
||
</div>
|
||
<div v-if="popupForm.popups.length === 0" class="popup-empty">
|
||
<el-empty description="暂无弹窗数据">
|
||
<el-button type="primary" @click="addPopup">添加第一条弹窗</el-button>
|
||
</el-empty>
|
||
</div>
|
||
<div v-for="(item, index) in popupForm.popups" :key="index" class="popup-item">
|
||
<el-card shadow="hover" :class="['popup-card', { 'popup-offline': item.status === 0, 'popup-expanded': item.expanded }]">
|
||
<!-- 简洁行显示 -->
|
||
<div class="popup-simple-row" @click="togglePopupExpand(index)">
|
||
<div class="popup-simple-left">
|
||
<span class="popup-index">{{ index + 1 }}</span>
|
||
<div class="popup-simple-info">
|
||
<span v-if="item.link" class="popup-simple-link">链接: {{ item.link.length > 30 ? item.link.slice(0, 30) + '...' : item.link }}</span>
|
||
<span class="popup-simple-position">排序: {{ item.sort || 0 }}</span>
|
||
</div>
|
||
<img v-if="item.imgUrl" :src="item.imgUrl" class="popup-thumb" />
|
||
</div>
|
||
<div class="popup-simple-right">
|
||
<el-tag :type="item.status === 1 ? 'success' : 'danger'" size="small">
|
||
{{ item.status === 1 ? '已上线' : '已下线' }}
|
||
</el-tag>
|
||
<el-switch
|
||
v-model="item.status"
|
||
:active-value="1"
|
||
:inactive-value="0"
|
||
size="mini"
|
||
active-color="#67C23A"
|
||
inactive-color="#F56C6C"
|
||
@change="handlePopupStatusChange(index)"
|
||
@click.native.stop
|
||
style="margin: 0 12px;">
|
||
</el-switch>
|
||
<el-button
|
||
type="danger"
|
||
icon="el-icon-delete"
|
||
size="mini"
|
||
circle
|
||
@click.stop="removePopup(index)">
|
||
</el-button>
|
||
<i
|
||
:class="item.expanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
|
||
class="popup-expand-icon">
|
||
</i>
|
||
</div>
|
||
</div>
|
||
<!-- 展开编辑内容 -->
|
||
<div v-show="item.expanded" class="popup-card-content">
|
||
<el-divider style="margin: 16px 0;"></el-divider>
|
||
<el-form label-width="80px" label-position="top">
|
||
<el-row :gutter="20">
|
||
<el-col :span="6">
|
||
<el-form-item label="跳转链接">
|
||
|
||
<selecturl v-model="item.link"></selecturl>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="排序">
|
||
<el-input-number
|
||
v-model="item.sort"
|
||
:min="0"
|
||
:max="999"
|
||
controls-position="right"
|
||
placeholder="排序值(数字越小越靠前)"
|
||
style="width: 100%;">
|
||
</el-input-number>
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="状态">
|
||
<el-switch v-model="item.status" :active-value="1" :inactive-value="0" />
|
||
</el-form-item>
|
||
</el-col>
|
||
<el-col :span="6">
|
||
<el-form-item label="弹窗图片">
|
||
<el-upload
|
||
:ref="'popupUpload' + index"
|
||
:action="uploadFileUrl"
|
||
:headers="uploadHeaders"
|
||
:show-file-list="false"
|
||
:on-success="(res, file) => handlePopupImgSuccess(res, file, index)"
|
||
:on-error="(err, file) => handlePopupImgError(err, file, index)"
|
||
:before-upload="file => beforePopupImgUpload(file, index)"
|
||
:key="'upload_' + index + '_' + (item.imgUrl ? 'has' : 'no')"
|
||
class="popup-img-uploader"
|
||
name="file">
|
||
<div style="position:relative;display:inline-block;">
|
||
<img v-if="item.imgUrl" :src="item.imgUrl" class="popup-img" style="cursor:pointer" title="点击替换图片" />
|
||
<el-button v-if="item.imgUrl" class="popup-img-delete-btn" type="danger" icon="el-icon-close" circle size="mini" @click.stop="removePopupImg(index)" style="position:absolute;top:2px;right:2px;padding:0;width:22px;height:22px;" title="删除图片"></el-button>
|
||
<i v-else class="el-icon-plus popup-uploader-icon" style="cursor:pointer" title="点击上传图片" />
|
||
</div>
|
||
</el-upload>
|
||
</el-form-item>
|
||
</el-col>
|
||
</el-row>
|
||
</el-form>
|
||
<div class="popup-edit-actions">
|
||
<el-button type="primary" size="small" @click="savePopupItem(index)">
|
||
<i class="el-icon-check"></i> 保存
|
||
</el-button>
|
||
<el-button size="small" @click="togglePopupExpand(index)">
|
||
<i class="el-icon-close"></i> 收起
|
||
</el-button>
|
||
</div>
|
||
</div>
|
||
</el-card>
|
||
</div>
|
||
</div>
|
||
</el-tab-pane>
|
||
</el-tabs>
|
||
<el-button type="primary" style="margin-top: 24px;" @click="saveAllConfig">保存全部配置</el-button>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { listSiteConfig, getSiteConfig, delSiteConfig, addSiteConfig, updateSiteConfig } from "@/api/system/SiteConfig"
|
||
import { getToken } from "@/utils/auth"
|
||
import selecturl from "./selecturl.vue"
|
||
|
||
|
||
|
||
|
||
|
||
|
||
export default {
|
||
name: 'SysSeting',
|
||
components: {
|
||
selecturl
|
||
},
|
||
data() {
|
||
return {
|
||
// 遮罩层
|
||
loading: true,
|
||
activeTab: 'base',
|
||
SiteConfigList: [],
|
||
config_one: {},
|
||
config_two: {},
|
||
config_three: {},
|
||
config_four: {},
|
||
config_five: {},
|
||
config_six: {},
|
||
total: 0, // 系统配置表格数据
|
||
token: getToken(), // 获取token用于上传
|
||
// 上传配置
|
||
|
||
uploadFileUrl:process.env.NODE_ENV === 'development'?"/dev-api/common/upload":"/prod-api/common/upload",
|
||
uploadHeaders: {
|
||
Authorization: "Bearer " + getToken(),
|
||
},
|
||
queryParams: {
|
||
pageNum: 1,
|
||
pageSize: 30,
|
||
name: null,
|
||
status: null
|
||
},
|
||
// 基本信息
|
||
baseForm: {
|
||
phone: '',
|
||
startTime: '',
|
||
endTime: '',
|
||
mstime: '',
|
||
marginRate: 10,
|
||
consumption: 0,
|
||
servicefee: 0,
|
||
material_commissions: 10,
|
||
member_discount: 80,
|
||
recharge_discount: 0,
|
||
consumption_deduction: 0,
|
||
service_deduction: 0,
|
||
orderScore: 100,
|
||
hotwords: ['水电维修', '家电清洗', '灯具维修', '墙面翻新', '门窗家具', '疏通维修', '防水维修'],
|
||
qrUrl: ''
|
||
},
|
||
// 文本配置
|
||
textForm: {
|
||
company: '',
|
||
intro: '',
|
||
marginDesc: ''
|
||
},
|
||
memberForm:{
|
||
member: '',
|
||
memberRule: '',
|
||
recharge: '' // 新增充值规则说明
|
||
},
|
||
// 评价标签配置
|
||
ratingForm: {
|
||
service: ['服务态度好', '技术专业', '准时守信', '认真负责', '价格合理'],
|
||
goods: ['商品质量好', '包装完整', '物流快速', '性价比高', '描述相符']
|
||
},
|
||
// 下单时间配置
|
||
orderTimes: [
|
||
|
||
],
|
||
// 时间配置
|
||
timeForm: {
|
||
withdrawDays: ['8号', '18号', '28号'],
|
||
autoOrderMinutes: 10,
|
||
cancelOrderDays: 7
|
||
},
|
||
|
||
config_seven: {},
|
||
noticeForm: {
|
||
notice: []
|
||
},
|
||
// 通知公告筛选
|
||
noticeFilter: '',
|
||
activityForm: {
|
||
activities: []
|
||
},
|
||
config_eight: {},
|
||
config_nine: {}, // 新增站内地址配置
|
||
siteLinks: [], // 站内地址数据
|
||
config_ten: {}, // 上门标准配置
|
||
serviceStandardForm: {
|
||
serviceStandard: ''
|
||
},
|
||
config_eleven: {}, // 首页弹窗配置
|
||
popupForm: {
|
||
popups: []
|
||
},
|
||
|
||
}
|
||
},
|
||
computed: {
|
||
// 过滤后的公告列表
|
||
filteredNoticeList() {
|
||
let filtered = this.noticeForm.notice;
|
||
|
||
// 根据分类筛选
|
||
if (this.noticeFilter) {
|
||
filtered = filtered.filter(item => item.category === this.noticeFilter);
|
||
}
|
||
|
||
// 添加原始索引,用于在过滤后仍能正确操作原数组
|
||
return filtered.map((item, index) => {
|
||
const originalIndex = this.noticeForm.notice.findIndex(notice => notice === item);
|
||
return {
|
||
...item,
|
||
originalIndex: originalIndex
|
||
};
|
||
});
|
||
}
|
||
},
|
||
created() {
|
||
this.getList()
|
||
},
|
||
methods: {
|
||
/** 查询系统配置列表 */
|
||
getList() {
|
||
this.loading = true
|
||
listSiteConfig(this.queryParams).then(response => {
|
||
this.SiteConfigList = response.rows
|
||
|
||
// config_one 基本信息
|
||
this.config_one = response.rows.find(item => item.name === 'config_one')
|
||
if (this.config_one && this.config_one.value) {
|
||
const configOneObj = JSON.parse(this.config_one.value)
|
||
this.baseForm = {
|
||
phone: configOneObj.phone || '',
|
||
startTime: configOneObj.loot_start || '',
|
||
endTime: configOneObj.loot_end || '',
|
||
mstime: configOneObj.mstime || '',
|
||
marginRate: configOneObj.margin || 10,
|
||
consumption: configOneObj.consumption || 0,
|
||
servicefee: configOneObj.servicefee || 0,
|
||
material_commissions: configOneObj.material_commissions || 10,
|
||
member_discount: configOneObj.member_discount || 80,
|
||
recharge_discount: configOneObj.recharge_discount || 0,
|
||
consumption_deduction: configOneObj.consumption_deduction || 0,
|
||
service_deduction: configOneObj.service_deduction || 0,
|
||
orderScore: configOneObj.orderScore || 100,
|
||
hotwords: configOneObj.hot || [],
|
||
qrUrl: configOneObj.kf || '',
|
||
}
|
||
}
|
||
|
||
// config_two 文本配置
|
||
this.config_two = response.rows.find(item => item.name === 'config_two')
|
||
if (this.config_two && this.config_two.value) {
|
||
const configTwoObj = JSON.parse(this.config_two.value)
|
||
this.textForm = {
|
||
company: configTwoObj.name || '',
|
||
intro: configTwoObj.brief || '',
|
||
marginDesc: configTwoObj.money_explain || ''
|
||
}
|
||
}
|
||
|
||
// config_five 会员配置
|
||
this.config_five = response.rows.find(item => item.name === 'config_five')
|
||
if (this.config_five && this.config_five.value) {
|
||
const configfiveObj = JSON.parse(this.config_five.value)
|
||
this.memberForm = {
|
||
member: configfiveObj.member || '',
|
||
memberRule: configfiveObj.memberRule || '',
|
||
recharge: configfiveObj.recharge || ''
|
||
}
|
||
}
|
||
|
||
// config_six 评价标签配置
|
||
this.config_six = response.rows.find(item => item.name === 'config_six')
|
||
if (this.config_six && this.config_six.value) {
|
||
const configSixObj = JSON.parse(this.config_six.value)
|
||
this.ratingForm = {
|
||
service: configSixObj.service || ['服务态度好', '技术专业', '准时守信', '认真负责', '价格合理'],
|
||
goods: configSixObj.goods || ['商品质量好', '包装完整', '物流快速', '性价比高', '描述相符']
|
||
}
|
||
}
|
||
|
||
// config_three 下单时间配置
|
||
this.config_three = response.rows.find(item => item.name === 'config_three')
|
||
this.orderTimes = []
|
||
if (this.config_three && this.config_three.value) {
|
||
let _data = JSON.parse(this.config_three.value)
|
||
if (_data && _data.time) {
|
||
for (let key in _data.time) {
|
||
const [start, end] = _data.time[key].key.split('-');
|
||
this.orderTimes.push({
|
||
start: start || '',
|
||
end: end || '',
|
||
count: _data.time[key].num || 99
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
// config_four 时间配置
|
||
this.config_four = response.rows.find(item => item.name === 'config_four')
|
||
if (this.config_four && this.config_four.value) {
|
||
const configFourObj = JSON.parse(this.config_four.value)
|
||
this.timeForm = {
|
||
withdrawDays: configFourObj.time || [],
|
||
autoOrderMinutes: configFourObj.order_time || 10,
|
||
cancelOrderDays: configFourObj.remove_time || 7
|
||
}
|
||
}
|
||
|
||
// config_seven 通知公告配置
|
||
this.config_seven = response.rows.find(item => item.name === 'config_seven')
|
||
if (this.config_seven && this.config_seven.value) {
|
||
const configNoticeObj = JSON.parse(this.config_seven.value)
|
||
this.noticeForm.notice = (configNoticeObj.notice || []).map(item => ({
|
||
...item,
|
||
category: item.category || 'home', // 兼容旧数据,默认为首页通知公告
|
||
expanded: false // 从后端加载的数据默认不展开
|
||
}))
|
||
}
|
||
|
||
// config_eight 活动专区配置
|
||
this.config_eight = response.rows.find(item => item.name === 'config_eight')
|
||
if (this.config_eight && this.config_eight.value) {
|
||
const configEightObj = JSON.parse(this.config_eight.value)
|
||
this.activityForm.activities = (configEightObj.activities || []).map(item => ({
|
||
...item,
|
||
status: typeof item.status === 'undefined' ? 1 : item.status,
|
||
link: item.link || '', // 兼容旧数据,默认为空字符串
|
||
position: typeof item.position === 'undefined' ? 0 : item.position, // 兼容旧数据,默认显示位置为0
|
||
expanded: false // 从后端加载的数据默认不展开
|
||
}))
|
||
}
|
||
|
||
// config_nine 站内地址配置
|
||
this.config_nine = response.rows.find(item => item.name === 'config_nine')
|
||
if (this.config_nine && this.config_nine.value) {
|
||
try {
|
||
this.siteLinks = JSON.parse(this.config_nine.value).siteLinks || [];
|
||
} catch (e) {
|
||
this.siteLinks = [];
|
||
}
|
||
} else {
|
||
this.siteLinks = [];
|
||
}
|
||
|
||
// config_ten 上门标准配置
|
||
this.config_ten = response.rows.find(item => item.name === 'config_ten')
|
||
if (this.config_ten && this.config_ten.value) {
|
||
try {
|
||
const configTenObj = JSON.parse(this.config_ten.value)
|
||
this.serviceStandardForm = {
|
||
serviceStandard: configTenObj.serviceStandard || ''
|
||
}
|
||
} catch (e) {
|
||
this.serviceStandardForm = { serviceStandard: '' }
|
||
}
|
||
} else {
|
||
this.serviceStandardForm = { serviceStandard: '' }
|
||
}
|
||
|
||
// config_eleven 首页弹窗配置
|
||
this.config_eleven = response.rows.find(item => item.name === 'config_shiyi')
|
||
|
||
if (this.config_eleven && this.config_eleven.value) {
|
||
try {
|
||
const configElevenObj = JSON.parse(this.config_eleven.value)
|
||
|
||
this.popupForm.popups = (configElevenObj.popups || []).map(item => ({
|
||
...item,
|
||
status: typeof item.status === 'undefined' ? 1 : item.status,
|
||
link: item.link || '',
|
||
sort: typeof item.sort === 'undefined' ? 0 : item.sort,
|
||
expanded: false
|
||
}))
|
||
|
||
} catch (e) {
|
||
console.error('解析弹窗配置失败:', e);
|
||
this.popupForm.popups = []
|
||
}
|
||
} else {
|
||
this.popupForm.popups = []
|
||
}
|
||
|
||
this.total = response.total
|
||
this.loading = false
|
||
})
|
||
},
|
||
// 基本信息
|
||
submitBase() {
|
||
this.$message.success('提交成功(模拟)')
|
||
},
|
||
resetBase() {
|
||
this.$refs.baseForm && this.$refs.baseForm.resetFields && this.$refs.baseForm.resetFields()
|
||
// 手动重置mstime字段
|
||
this.baseForm.mstime = ''
|
||
},
|
||
handleQrChange(file) {
|
||
// 模拟上传二维码
|
||
const reader = new FileReader()
|
||
reader.onload = e => {
|
||
this.baseForm.qrUrl = e.target.result
|
||
}
|
||
reader.readAsDataURL(file.raw)
|
||
},
|
||
beforeQrUpload(file) {
|
||
const isImg = file.type.startsWith('image/')
|
||
if (!isImg) {
|
||
this.$message.error('只能上传图片格式')
|
||
}
|
||
return isImg
|
||
},
|
||
// 文本配置
|
||
submitText() {
|
||
this.$message.success('提交成功(模拟)')
|
||
},
|
||
resetText() {
|
||
this.textForm = { company: '', intro: '', marginDesc: '' }
|
||
},
|
||
// 下单时间配置
|
||
updateTimeRange(idx) {
|
||
const item = this.orderTimes[idx];
|
||
if (item.start && item.end && item.start >= item.end) {
|
||
this.$message.error('结束时间必须大于开始时间');
|
||
item.end = '';
|
||
}
|
||
},
|
||
addOrderTime() {
|
||
this.orderTimes.push({ start: '', end: '', count: 99 });
|
||
},
|
||
removeOrderTime(idx) {
|
||
this.orderTimes.splice(idx, 1)
|
||
},
|
||
submitOrderTime() {
|
||
// 提交时拼接时间段
|
||
const timeList = this.orderTimes.map(item => ({
|
||
time: `${item.start}-${item.end}`,
|
||
count: item.count
|
||
}));
|
||
// 这里可以将 timeList 发送给后端
|
||
this.$message.success('提交成功(模拟)')
|
||
},
|
||
resetOrderTime() {
|
||
this.orderTimes = [
|
||
|
||
]
|
||
},
|
||
// 时间配置
|
||
submitTime() {
|
||
this.$message.success('提交成功(模拟)')
|
||
},
|
||
resetTime() {
|
||
this.timeForm = { withdrawDays: ['8号', '18号', '28号'], autoOrderMinutes: 10, cancelOrderDays: 7 }
|
||
},
|
||
resetMember() {
|
||
this.memberForm = { member: '', memberRule: '', recharge: '' }
|
||
},
|
||
resetRating() {
|
||
this.ratingForm = {
|
||
service: ['服务态度好', '技术专业', '准时守信', '认真负责', '价格合理'],
|
||
goods: ['商品质量好', '包装完整', '物流快速', '性价比高', '描述相符']
|
||
}
|
||
},
|
||
resetServiceStandard() {
|
||
this.serviceStandardForm = { serviceStandard: '' }
|
||
},
|
||
resetPopup() {
|
||
this.popupForm.popups = []
|
||
},
|
||
async saveAllConfig(e) {
|
||
// 1. 组装 config_one
|
||
const config_one = {
|
||
phone: this.baseForm.phone,
|
||
loot_start: this.baseForm.startTime,
|
||
loot_end: this.baseForm.endTime,
|
||
mstime: this.baseForm.mstime,
|
||
margin: this.baseForm.marginRate,
|
||
consumption: this.baseForm.consumption,
|
||
servicefee: this.baseForm.servicefee,
|
||
material_commissions: this.baseForm.material_commissions,
|
||
member_discount: this.baseForm.member_discount,
|
||
recharge_discount: this.baseForm.recharge_discount,
|
||
consumption_deduction: this.baseForm.consumption_deduction,
|
||
service_deduction: this.baseForm.service_deduction,
|
||
orderScore: this.baseForm.orderScore,
|
||
hot: this.baseForm.hotwords,
|
||
kf: this.baseForm.qrUrl
|
||
};
|
||
// 2. 组装 config_two
|
||
const config_two = {
|
||
name: this.textForm.company,
|
||
brief: this.textForm.intro,
|
||
money_explain: this.textForm.marginDesc
|
||
};
|
||
// 3. 组装 config_three
|
||
const config_three = {
|
||
time: this.orderTimes.map(item => ({
|
||
key: `${item.start}-${item.end}`,
|
||
num: item.count
|
||
}))
|
||
};
|
||
// 4. 组装 config_four
|
||
const config_four = {
|
||
time: this.timeForm.withdrawDays,
|
||
order_time: this.timeForm.autoOrderMinutes,
|
||
remove_time: this.timeForm.cancelOrderDays
|
||
};
|
||
// 5. 组装 config_five
|
||
const config_five = {
|
||
member: this.memberForm.member,
|
||
memberRule: this.memberForm.memberRule,
|
||
recharge: this.memberForm.recharge
|
||
};
|
||
// 6. 组装 config_six
|
||
const config_six = {
|
||
service: this.ratingForm.service,
|
||
goods: this.ratingForm.goods
|
||
};
|
||
// 组装 config_seven
|
||
const config_seven = {
|
||
notice: this.noticeForm.notice.map(item => ({
|
||
title: item.title || '',
|
||
content: item.content || '',
|
||
link: item.link || '',
|
||
sort: parseInt(item.sort) || 0,
|
||
status: parseInt(item.status) || 1,
|
||
category: item.category || 'home'
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
try {
|
||
if(e=='1'){
|
||
await updateSiteConfig({ name: 'config_one',id:this.config_one.id, value: JSON.stringify(config_one) });
|
||
}else if(e=='2'){
|
||
await updateSiteConfig({ name: 'config_two',id:this.config_two.id, value: JSON.stringify(config_two) });
|
||
}else if(e=='3'){
|
||
await updateSiteConfig({ name: 'config_three',id:this.config_three.id, value: JSON.stringify(config_three) });
|
||
}else if(e=='4'){
|
||
await updateSiteConfig({ name: 'config_four',id:this.config_four.id, value: JSON.stringify(config_four) });
|
||
}else if(e=='5'){
|
||
await updateSiteConfig({ name: 'config_five',id:this.config_five.id, value: JSON.stringify(config_five) });
|
||
}else if(e=='6'){
|
||
if (!this.config_six || !this.config_six.id) {
|
||
await addSiteConfig({ name: 'config_six', value: JSON.stringify(config_six), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_six', id: this.config_six.id, value: JSON.stringify(config_six) });
|
||
}
|
||
}else if(e=='notice'){
|
||
console.log('保存通知公告配置');
|
||
console.log('config_seven数据:', config_seven);
|
||
console.log('当前config_seven配置:', this.config_seven);
|
||
|
||
if (!this.config_seven || !this.config_seven.id) {
|
||
console.log('新增通知公告配置');
|
||
const response = await addSiteConfig({
|
||
name: 'config_seven',
|
||
value: JSON.stringify(config_seven),
|
||
status: 1
|
||
});
|
||
console.log('新增响应:', response);
|
||
} else {
|
||
console.log('更新通知公告配置,ID:', this.config_seven.id);
|
||
const response = await updateSiteConfig({
|
||
name: 'config_seven',
|
||
id: this.config_seven.id,
|
||
value: JSON.stringify(config_seven)
|
||
});
|
||
console.log('更新响应:', response);
|
||
}
|
||
}else if(e=='activity'){
|
||
const config_eight = {
|
||
activities: this.activityForm.activities.map(item => ({
|
||
title: item.title || '',
|
||
content: item.content || '',
|
||
link: item.link || '',
|
||
imgUrl: item.imgUrl || '',
|
||
status: parseInt(item.status) || 0,
|
||
position: parseInt(item.position) || 0
|
||
}))
|
||
};
|
||
if (!this.config_eight || !this.config_eight.id) {
|
||
await addSiteConfig({ name: 'config_eight', value: JSON.stringify(config_eight), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_eight', id: this.config_eight.id, value: JSON.stringify(config_eight) });
|
||
}
|
||
}else if(e=='siteLinks'){
|
||
const config_nine = { siteLinks: this.siteLinks };
|
||
try {
|
||
if (!this.config_nine || !this.config_nine.id) {
|
||
await addSiteConfig({ name: 'config_nine', value: JSON.stringify(config_nine), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_nine', id: this.config_nine.id, value: JSON.stringify(config_nine) });
|
||
}
|
||
this.$message.success('站内地址已保存!');
|
||
await this.getList();
|
||
} catch (error) {
|
||
this.$message.error('保存失败,请重试');
|
||
}
|
||
}else if(e=='serviceStandard'){
|
||
const config_ten = {
|
||
serviceStandard: this.serviceStandardForm.serviceStandard
|
||
};
|
||
try {
|
||
if (!this.config_ten || !this.config_ten.id) {
|
||
await addSiteConfig({ name: 'config_ten', value: JSON.stringify(config_ten), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_ten', id: this.config_ten.id, value: JSON.stringify(config_ten) });
|
||
}
|
||
this.$message.success('上门标准已保存!');
|
||
await this.getList();
|
||
} catch (error) {
|
||
this.$message.error('保存失败,请重试');
|
||
}
|
||
return; // 单独保存时直接返回,不执行后面的通用保存逻辑
|
||
}else if(e=='popup'){
|
||
const config_eleven = {
|
||
popups: this.popupForm.popups.map(item => ({
|
||
link: item.link || '',
|
||
imgUrl: item.imgUrl || '',
|
||
status: parseInt(item.status) || 0,
|
||
sort: parseInt(item.sort) || 0
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
|
||
try {
|
||
if (!this.config_eleven || !this.config_eleven.id) {
|
||
const response = await addSiteConfig({
|
||
name: 'config_shiyi',
|
||
value: JSON.stringify(config_eleven),
|
||
status: 1
|
||
});
|
||
this.config_eleven = response.data;
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_shiyi',
|
||
id: this.config_eleven.id,
|
||
value: JSON.stringify(config_eleven)
|
||
});
|
||
}
|
||
this.$message.success('首页弹窗已保存!');
|
||
await this.getList();
|
||
} catch (error) {
|
||
console.error('保存弹窗失败:', error);
|
||
this.$message.error('保存失败,请重试');
|
||
}
|
||
return; // 单独保存时直接返回,不执行后面的通用保存逻辑
|
||
}
|
||
|
||
// 如果没有指定具体的配置类型,则保存所有配置
|
||
if (!e) {
|
||
this.$message.success('保存成功!');
|
||
await this.getList();
|
||
}
|
||
} catch (error) {
|
||
console.error('保存失败:', error);
|
||
console.error('错误详情:', error.response || error.message || error);
|
||
|
||
let errorMessage = '保存失败,请重试';
|
||
if (error.response && error.response.data && error.response.data.msg) {
|
||
errorMessage = `保存失败:${error.response.data.msg}`;
|
||
} else if (error.message) {
|
||
errorMessage = `保存失败:${error.message}`;
|
||
}
|
||
|
||
this.$message.error(errorMessage);
|
||
}
|
||
},
|
||
addNotice() {
|
||
// 找到当前最大的排序值
|
||
const maxSort = this.noticeForm.notice.length > 0
|
||
? Math.max(...this.noticeForm.notice.map(item => item.sort || 0))
|
||
: -1;
|
||
|
||
this.noticeForm.notice.push({
|
||
title: '',
|
||
content: '',
|
||
link: '',
|
||
sort: maxSort + 1,
|
||
status: 1,
|
||
category: 'home', // 默认为首页通知公告
|
||
expanded: true // 新添加的公告默认展开以便编辑
|
||
})
|
||
},
|
||
|
||
// 切换公告展开/收起状态
|
||
toggleNoticeExpand(index) {
|
||
this.$set(this.noticeForm.notice[index], 'expanded', !this.noticeForm.notice[index].expanded);
|
||
},
|
||
|
||
// 保存单个公告项
|
||
async saveNoticeItem(index) {
|
||
const item = this.noticeForm.notice[index];
|
||
|
||
console.log('开始保存公告,索引:', index);
|
||
console.log('保存的公告数据:', item);
|
||
|
||
// 验证必填项
|
||
if (!item.title || !item.title.trim()) {
|
||
this.$message.error('请输入公告标题');
|
||
return;
|
||
}
|
||
|
||
if (!item.content || !item.content.trim()) {
|
||
this.$message.error('请输入公告内容');
|
||
return;
|
||
}
|
||
|
||
// 验证分类
|
||
if (!item.category) {
|
||
this.$set(this.noticeForm.notice[index], 'category', 'home');
|
||
}
|
||
|
||
try {
|
||
// 构造保存数据
|
||
const config_seven = {
|
||
notice: this.noticeForm.notice.map(noticeItem => ({
|
||
title: noticeItem.title || '',
|
||
content: noticeItem.content || '',
|
||
link: noticeItem.link || '',
|
||
sort: parseInt(noticeItem.sort) || 0,
|
||
status: parseInt(noticeItem.status) || 1,
|
||
category: noticeItem.category || 'home'
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
|
||
console.log('准备保存的配置数据:', config_seven);
|
||
console.log('config_seven信息:', this.config_seven);
|
||
|
||
let response;
|
||
if (!this.config_seven || !this.config_seven.id) {
|
||
console.log('新增配置数据');
|
||
response = await addSiteConfig({
|
||
name: 'config_seven',
|
||
value: JSON.stringify(config_seven),
|
||
status: 1
|
||
});
|
||
console.log('新增响应:', response);
|
||
} else {
|
||
console.log('更新配置数据,ID:', this.config_seven.id);
|
||
response = await updateSiteConfig({
|
||
name: 'config_seven',
|
||
id: this.config_seven.id,
|
||
value: JSON.stringify(config_seven)
|
||
});
|
||
console.log('更新响应:', response);
|
||
}
|
||
|
||
this.$message.success('公告保存成功!');
|
||
|
||
// 重新获取数据
|
||
await this.getList();
|
||
|
||
// 保存成功后收起编辑区域
|
||
this.$set(this.noticeForm.notice[index], 'expanded', false);
|
||
|
||
} catch (error) {
|
||
console.error('保存公告失败:', error);
|
||
console.error('错误详情:', error.response || error.message || error);
|
||
this.$message.error(`保存失败:${error.message || '请重试'}`);
|
||
}
|
||
},
|
||
async removeNotice(index) {
|
||
const item = this.noticeForm.notice[index];
|
||
const title = item ? (item.title || '未设置标题') : '公告';
|
||
|
||
try {
|
||
await this.$confirm(`确定删除公告"${title}"吗?`, '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
});
|
||
|
||
// 删除公告
|
||
this.noticeForm.notice.splice(index, 1);
|
||
|
||
// 重新排序
|
||
this.noticeForm.notice.forEach((item, idx) => {
|
||
item.sort = idx;
|
||
});
|
||
|
||
// 保存到后端
|
||
try {
|
||
const config_seven = {
|
||
notice: this.noticeForm.notice.map(item => ({
|
||
title: item.title || '',
|
||
content: item.content || '',
|
||
link: item.link || '',
|
||
sort: parseInt(item.sort) || 0,
|
||
status: parseInt(item.status) || 1,
|
||
category: item.category || 'home'
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
|
||
if (!this.config_seven || !this.config_seven.id) {
|
||
await addSiteConfig({
|
||
name: 'config_seven',
|
||
value: JSON.stringify(config_seven),
|
||
status: 1
|
||
});
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_seven',
|
||
id: this.config_seven.id,
|
||
value: JSON.stringify(config_seven)
|
||
});
|
||
}
|
||
|
||
this.$message.success('删除成功');
|
||
await this.getList();
|
||
} catch (error) {
|
||
console.error('删除保存失败:', error);
|
||
this.$message.error('删除失败,请重试');
|
||
// 回滚删除操作
|
||
this.getList();
|
||
}
|
||
} catch (error) {
|
||
this.$message.info('已取消删除');
|
||
}
|
||
},
|
||
resetNotice() {
|
||
this.noticeForm.notice = []
|
||
},
|
||
|
||
// 获取分类名称
|
||
getCategoryName(category) {
|
||
const categoryMap = {
|
||
'home': '首页通知公告',
|
||
'activity': '活动通知公告'
|
||
};
|
||
return categoryMap[category] || '首页通知公告';
|
||
},
|
||
|
||
// 获取分类标签类型
|
||
getCategoryTagType(category) {
|
||
const typeMap = {
|
||
'home': 'primary',
|
||
'activity': 'warning'
|
||
};
|
||
return typeMap[category] || 'primary';
|
||
},
|
||
|
||
// 处理状态变化
|
||
async handleStatusChange(index) {
|
||
const item = this.noticeForm.notice[index];
|
||
if (!item) return;
|
||
|
||
const newStatus = item.status;
|
||
const oldStatus = newStatus === 1 ? 0 : 1;
|
||
const statusText = newStatus === 1 ? '上线' : '下线';
|
||
const currentTitle = item.title || '未设置标题';
|
||
|
||
// 添加确认对话框
|
||
try {
|
||
await this.$confirm(`确定要${statusText}公告"${currentTitle}"吗?`, '状态变更确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: newStatus === 1 ? 'success' : 'warning'
|
||
});
|
||
} catch (error) {
|
||
// 用户取消了操作,回滚状态
|
||
this.$set(this.noticeForm.notice[index], 'status', oldStatus);
|
||
return;
|
||
}
|
||
|
||
// 用户确认,保存到后端
|
||
try {
|
||
const config_seven = {
|
||
notice: this.noticeForm.notice.map(item => ({
|
||
title: item.title,
|
||
content: item.content,
|
||
link: item.link,
|
||
sort: item.sort,
|
||
status: item.status,
|
||
category: item.category || 'home'
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
|
||
if (!this.config_seven || !this.config_seven.id) {
|
||
await addSiteConfig({ name: 'config_seven', value: JSON.stringify(config_seven), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_seven', id: this.config_seven.id, value: JSON.stringify(config_seven) });
|
||
}
|
||
|
||
this.$message.success(`公告${statusText}成功!`);
|
||
|
||
// 重新获取数据以确保同步
|
||
this.getList();
|
||
} catch (error) {
|
||
console.error('状态更新失败:', error);
|
||
// 保存失败,回滚状态
|
||
this.$set(this.noticeForm.notice[index], 'status', oldStatus);
|
||
this.$message.error('状态更新失败,请重试');
|
||
}
|
||
},
|
||
handleCategoryChange(val, item, index) {
|
||
// 强制响应式,防止item.category不是响应式
|
||
this.$set(this.noticeForm.notice[index], 'category', val)
|
||
},
|
||
addActivity() {
|
||
// 找到当前最大的显示位置值
|
||
const maxPosition = this.activityForm.activities.length > 0
|
||
? Math.max(...this.activityForm.activities.map(item => item.position || 0))
|
||
: -1;
|
||
|
||
this.activityForm.activities.push({
|
||
title: '',
|
||
content: '',
|
||
link: '',
|
||
imgUrl: '',
|
||
status: 1,
|
||
position: maxPosition + 1, // 新活动的显示位置自动设为最大值+1
|
||
expanded: false // 新添加的活动默认不展开
|
||
});
|
||
},
|
||
removeActivity(index) {
|
||
this.activityForm.activities.splice(index, 1);
|
||
},
|
||
resetActivity() {
|
||
this.activityForm.activities = [];
|
||
},
|
||
beforeActivityImgUpload(file, index) {
|
||
console.log('准备上传图片:', file.name, file.type, file.size);
|
||
|
||
// 检查文件类型
|
||
const isImg = file.type.startsWith('image/');
|
||
if (!isImg) {
|
||
this.$message.error('只能上传图片格式');
|
||
return false;
|
||
}
|
||
|
||
// 检查文件大小(限制10MB)
|
||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||
if (!isLt10M) {
|
||
this.$message.error('图片大小不能超过10MB');
|
||
return false;
|
||
}
|
||
|
||
console.log('图片检查通过,开始上传...');
|
||
this.$message.info('图片上传中...');
|
||
|
||
return true;
|
||
},
|
||
handleActivityImgSuccess(res, file, index) {
|
||
console.log('图片上传成功回调:', res, file, index);
|
||
console.log('完整响应数据:', JSON.stringify(res, null, 2));
|
||
|
||
// 处理上传成功的响应
|
||
let url = '';
|
||
if (res && res.code === 200) {
|
||
// 标准的RuoYi响应格式
|
||
if (res.url) {
|
||
url = res.url;
|
||
} else if (res.fileName) {
|
||
url = res.fileName;
|
||
} else if (res.data && res.data.url) {
|
||
url = res.data.url;
|
||
} else if (res.data && res.data.fileName) {
|
||
url = res.data.fileName;
|
||
}
|
||
} else if (res) {
|
||
// 兼容其他格式
|
||
if (typeof res === 'string') {
|
||
url = res;
|
||
} else if (res.url) {
|
||
url = res.url;
|
||
} else if (res.fileName) {
|
||
url = res.fileName;
|
||
} else if (res.data) {
|
||
if (typeof res.data === 'string') {
|
||
url = res.data;
|
||
} else if (res.data.url) {
|
||
url = res.data.url;
|
||
} else if (res.data.fileName) {
|
||
url = res.data.fileName;
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('解析出的图片URL:', url);
|
||
|
||
// 如果是相对路径,自动拼接完整URL
|
||
if (url && !/^https?:\/\//.test(url)) {
|
||
// 使用VUE_APP_BASE_API或当前域名
|
||
const base = process.env.VUE_APP_BASE_API || window.location.origin;
|
||
url = base.replace(/\/$/, '') + (url.startsWith('/') ? url : '/' + url);
|
||
}
|
||
|
||
console.log('最终处理后的图片URL:', url);
|
||
|
||
if (url) {
|
||
// 确保索引有效
|
||
if (this.activityForm.activities[index]) {
|
||
this.$set(this.activityForm.activities[index], 'imgUrl', url);
|
||
this.$message.success('图片上传成功');
|
||
console.log('图片URL已设置到活动数据:', this.activityForm.activities[index]);
|
||
|
||
// 强制刷新上传组件
|
||
this.$forceUpdate();
|
||
} else {
|
||
console.error('活动索引无效:', index);
|
||
this.$message.error('活动索引无效');
|
||
}
|
||
} else {
|
||
console.error('无法从响应中获取图片URL');
|
||
console.error('响应数据结构:', res);
|
||
this.$message.error('图片上传失败:无法获取图片地址');
|
||
}
|
||
},
|
||
handleActivityImgError(err, file, index) {
|
||
console.error('图片上传失败:', err, file, index);
|
||
console.error('错误详情:', JSON.stringify(err, null, 2));
|
||
|
||
let errorMessage = '图片上传失败';
|
||
if (err.message) {
|
||
errorMessage += ':' + err.message;
|
||
} else if (err.response && err.response.data && err.response.data.msg) {
|
||
errorMessage += ':' + err.response.data.msg;
|
||
} else if (err.response && err.response.statusText) {
|
||
errorMessage += ':' + err.response.statusText;
|
||
} else {
|
||
errorMessage += ':网络错误';
|
||
}
|
||
|
||
this.$message.error(errorMessage);
|
||
},
|
||
// 删除图片
|
||
removeActivityImg(index) {
|
||
this.$confirm('确定要删除这张图片吗?', '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.$set(this.activityForm.activities[index], 'imgUrl', '');
|
||
this.$message.success('图片删除成功');
|
||
}).catch(() => {
|
||
this.$message.info('已取消删除');
|
||
});
|
||
},
|
||
toggleActivityExpand(index) {
|
||
this.$set(this.activityForm.activities[index], 'expanded', !this.activityForm.activities[index].expanded);
|
||
},
|
||
async saveActivityItem(index) {
|
||
const item = this.activityForm.activities[index];
|
||
|
||
// 验证必填项
|
||
if (!item.title || !item.title.trim()) {
|
||
this.$message.error('请输入活动标题');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 构造保存数据
|
||
const config_eight = {
|
||
activities: this.activityForm.activities.map(activityItem => ({
|
||
title: activityItem.title || '',
|
||
content: activityItem.content || '',
|
||
link: activityItem.link || '',
|
||
imgUrl: activityItem.imgUrl || '',
|
||
status: parseInt(activityItem.status) || 0,
|
||
position: parseInt(activityItem.position) || 0
|
||
}))
|
||
};
|
||
|
||
if (!this.config_eight || !this.config_eight.id) {
|
||
await addSiteConfig({
|
||
name: 'config_eight',
|
||
value: JSON.stringify(config_eight),
|
||
status: 1
|
||
});
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_eight',
|
||
id: this.config_eight.id,
|
||
value: JSON.stringify(config_eight)
|
||
});
|
||
}
|
||
|
||
this.$message.success('活动保存成功!');
|
||
|
||
// 重新获取数据
|
||
await this.getList();
|
||
|
||
// 保存成功后收起编辑区域
|
||
this.$set(this.activityForm.activities[index], 'expanded', false);
|
||
|
||
} catch (error) {
|
||
console.error('保存活动失败:', error);
|
||
this.$message.error(`保存失败:${error.message || '请重试'}`);
|
||
}
|
||
},
|
||
async handleActivityStatusChange(index) {
|
||
const item = this.activityForm.activities[index];
|
||
if (!item) return;
|
||
|
||
const newStatus = item.status;
|
||
const oldStatus = newStatus === 1 ? 0 : 1;
|
||
const statusText = newStatus === 1 ? '上线' : '下线';
|
||
const currentTitle = item.title || '未设置标题';
|
||
|
||
// 添加确认对话框
|
||
try {
|
||
await this.$confirm(`确定要${statusText}活动"${currentTitle}"吗?`, '状态变更确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: newStatus === 1 ? 'success' : 'warning'
|
||
});
|
||
} catch (error) {
|
||
// 用户取消了操作,回滚状态
|
||
this.$set(this.activityForm.activities[index], 'status', oldStatus);
|
||
return;
|
||
}
|
||
|
||
// 用户确认,保存到后端
|
||
try {
|
||
const config_eight = {
|
||
activities: this.activityForm.activities.map(item => ({
|
||
title: item.title || '',
|
||
content: item.content || '',
|
||
link: item.link || '',
|
||
imgUrl: item.imgUrl || '',
|
||
status: parseInt(item.status) || 0,
|
||
position: parseInt(item.position) || 0
|
||
}))
|
||
};
|
||
|
||
if (!this.config_eight || !this.config_eight.id) {
|
||
await addSiteConfig({ name: 'config_eight', value: JSON.stringify(config_eight), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_eight', id: this.config_eight.id, value: JSON.stringify(config_eight) });
|
||
}
|
||
|
||
this.$message.success(`活动${statusText}成功!`);
|
||
|
||
// 重新获取数据以确保同步
|
||
this.getList();
|
||
} catch (error) {
|
||
console.error('状态更新失败:', error);
|
||
// 保存失败,回滚状态
|
||
this.$set(this.activityForm.activities[index], 'status', oldStatus);
|
||
this.$message.error('状态更新失败,请重试');
|
||
}
|
||
},
|
||
|
||
async removeActivity(index) {
|
||
const item = this.activityForm.activities[index];
|
||
const title = item ? (item.title || '未设置标题') : '活动';
|
||
|
||
try {
|
||
await this.$confirm(`确定删除活动"${title}"吗?`, '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
});
|
||
|
||
// 删除活动
|
||
this.activityForm.activities.splice(index, 1);
|
||
|
||
// 保存到后端
|
||
try {
|
||
const config_eight = {
|
||
activities: this.activityForm.activities.map(item => ({
|
||
title: item.title || '',
|
||
content: item.content || '',
|
||
link: item.link || '',
|
||
imgUrl: item.imgUrl || '',
|
||
status: parseInt(item.status) || 0,
|
||
position: parseInt(item.position) || 0
|
||
}))
|
||
};
|
||
|
||
if (!this.config_eight || !this.config_eight.id) {
|
||
await addSiteConfig({
|
||
name: 'config_eight',
|
||
value: JSON.stringify(config_eight),
|
||
status: 1
|
||
});
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_eight',
|
||
id: this.config_eight.id,
|
||
value: JSON.stringify(config_eight)
|
||
});
|
||
}
|
||
|
||
this.$message.success('删除成功');
|
||
await this.getList();
|
||
} catch (error) {
|
||
console.error('删除保存失败:', error);
|
||
this.$message.error('删除失败,请重试');
|
||
// 回滚删除操作
|
||
this.getList();
|
||
}
|
||
} catch (error) {
|
||
this.$message.info('已取消删除');
|
||
}
|
||
},
|
||
addSiteLink() {
|
||
this.siteLinks.push({ name: '', url: '', remark: '', params: '' });
|
||
},
|
||
removeSiteLink(idx) {
|
||
this.siteLinks.splice(idx, 1);
|
||
},
|
||
async saveSiteLinks() {
|
||
const config_nine = { siteLinks: this.siteLinks };
|
||
try {
|
||
if (!this.config_nine || !this.config_nine.id) {
|
||
await addSiteConfig({ name: 'config_nine', value: JSON.stringify(config_nine), status: 1 });
|
||
} else {
|
||
await updateSiteConfig({ name: 'config_nine', id: this.config_nine.id, value: JSON.stringify(config_nine) });
|
||
}
|
||
this.$message.success('站内地址已保存!');
|
||
await this.getList();
|
||
} catch (error) {
|
||
this.$message.error('保存失败,请重试');
|
||
}
|
||
},
|
||
|
||
// 首页弹窗相关方法
|
||
addPopup() {
|
||
// 找到当前最大的排序值
|
||
const maxSort = this.popupForm.popups.length > 0
|
||
? Math.max(...this.popupForm.popups.map(item => item.sort || 0))
|
||
: -1;
|
||
|
||
const newPopup = {
|
||
link: '',
|
||
imgUrl: '',
|
||
sort: maxSort + 1,
|
||
status: 1,
|
||
expanded: true // 新添加的弹窗默认展开以便编辑
|
||
};
|
||
|
||
this.popupForm.popups.push(newPopup);
|
||
|
||
this.$message.success('弹窗添加成功!');
|
||
},
|
||
|
||
// 切换弹窗展开/收起状态
|
||
togglePopupExpand(index) {
|
||
this.$set(this.popupForm.popups[index], 'expanded', !this.popupForm.popups[index].expanded);
|
||
},
|
||
|
||
// 保存单个弹窗项
|
||
async savePopupItem(index) {
|
||
const item = this.popupForm.popups[index];
|
||
|
||
try {
|
||
// 构造保存数据
|
||
const config_eleven = {
|
||
popups: this.popupForm.popups.map(popupItem => ({
|
||
link: popupItem.link || '',
|
||
imgUrl: popupItem.imgUrl || '',
|
||
status: parseInt(popupItem.status) || 0,
|
||
sort: parseInt(popupItem.sort) || 0
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
|
||
if (!this.config_eleven || !this.config_eleven.id) {
|
||
const response = await addSiteConfig({
|
||
name: 'config_shiyi',
|
||
value: JSON.stringify(config_eleven),
|
||
status: 1
|
||
});
|
||
// 更新config_eleven的id
|
||
this.config_eleven = response.data;
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_shiyi',
|
||
id: this.config_eleven.id,
|
||
value: JSON.stringify(config_eleven)
|
||
});
|
||
}
|
||
|
||
this.$message.success('弹窗保存成功!');
|
||
|
||
// 重新获取数据
|
||
await this.getList();
|
||
|
||
// 保存成功后收起编辑区域
|
||
this.$set(this.popupForm.popups[index], 'expanded', false);
|
||
|
||
} catch (error) {
|
||
console.error('保存弹窗失败:', error);
|
||
this.$message.error(`保存失败:${error.message || '请重试'}`);
|
||
}
|
||
},
|
||
|
||
// 删除弹窗
|
||
async removePopup(index) {
|
||
const item = this.popupForm.popups[index];
|
||
|
||
try {
|
||
await this.$confirm(`确定删除弹窗吗?`, '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
});
|
||
|
||
// 删除弹窗
|
||
this.popupForm.popups.splice(index, 1);
|
||
|
||
// 重新排序
|
||
this.popupForm.popups.forEach((item, idx) => {
|
||
item.sort = idx;
|
||
});
|
||
|
||
// 保存到后端
|
||
try {
|
||
const config_eleven = {
|
||
popups: this.popupForm.popups.map(item => ({
|
||
link: item.link || '',
|
||
imgUrl: item.imgUrl || '',
|
||
status: parseInt(item.status) || 0,
|
||
sort: parseInt(item.sort) || 0
|
||
})).sort((a, b) => a.sort - b.sort)
|
||
};
|
||
|
||
if (!this.config_eleven || !this.config_eleven.id) {
|
||
if (this.popupForm.popups.length > 0) {
|
||
// 如果还有弹窗,创建新配置
|
||
const response = await addSiteConfig({
|
||
name: 'config_shiyi',
|
||
value: JSON.stringify(config_eleven),
|
||
status: 1
|
||
});
|
||
this.config_eleven = response.data;
|
||
}
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_shiyi',
|
||
id: this.config_eleven.id,
|
||
value: JSON.stringify(config_eleven)
|
||
});
|
||
}
|
||
|
||
this.$message.success('删除成功');
|
||
await this.getList();
|
||
} catch (error) {
|
||
console.error('删除保存失败:', error);
|
||
this.$message.error('删除失败,请重试');
|
||
// 回滚删除操作
|
||
this.getList();
|
||
}
|
||
} catch (error) {
|
||
this.$message.info('已取消删除');
|
||
}
|
||
},
|
||
|
||
// 重置弹窗
|
||
resetPopup() {
|
||
this.popupForm.popups = [];
|
||
},
|
||
|
||
// 处理弹窗状态变化
|
||
async handlePopupStatusChange(index) {
|
||
const item = this.popupForm.popups[index];
|
||
if (!item) return;
|
||
|
||
const newStatus = item.status;
|
||
const oldStatus = newStatus === 1 ? 0 : 1;
|
||
const statusText = newStatus === 1 ? '上线' : '下线';
|
||
|
||
// 添加确认对话框
|
||
try {
|
||
await this.$confirm(`确定要${statusText}弹窗吗?`, '状态变更确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: newStatus === 1 ? 'success' : 'warning'
|
||
});
|
||
} catch (error) {
|
||
// 用户取消了操作,回滚状态
|
||
this.$set(this.popupForm.popups[index], 'status', oldStatus);
|
||
return;
|
||
}
|
||
|
||
// 用户确认,保存到后端
|
||
try {
|
||
const config_eleven = {
|
||
popups: this.popupForm.popups.map(item => ({
|
||
link: item.link || '',
|
||
imgUrl: item.imgUrl || '',
|
||
status: parseInt(item.status) || 0,
|
||
sort: parseInt(item.sort) || 0
|
||
}))
|
||
};
|
||
|
||
if (!this.config_eleven || !this.config_eleven.id) {
|
||
const response = await addSiteConfig({
|
||
name: 'config_shiyi',
|
||
value: JSON.stringify(config_eleven),
|
||
status: 1
|
||
});
|
||
this.config_eleven = response.data;
|
||
} else {
|
||
await updateSiteConfig({
|
||
name: 'config_shiyi',
|
||
id: this.config_eleven.id,
|
||
value: JSON.stringify(config_eleven)
|
||
});
|
||
}
|
||
|
||
this.$message.success(`弹窗${statusText}成功!`);
|
||
|
||
// 重新获取数据以确保同步
|
||
this.getList();
|
||
} catch (error) {
|
||
console.error('状态更新失败:', error);
|
||
// 保存失败,回滚状态
|
||
this.$set(this.popupForm.popups[index], 'status', oldStatus);
|
||
this.$message.error('状态更新失败,请重试');
|
||
}
|
||
},
|
||
|
||
// 弹窗图片上传前检查
|
||
beforePopupImgUpload(file, index) {
|
||
console.log('准备上传弹窗图片:', file.name, file.type, file.size);
|
||
|
||
// 检查文件类型
|
||
const isImg = file.type.startsWith('image/');
|
||
if (!isImg) {
|
||
this.$message.error('只能上传图片格式');
|
||
return false;
|
||
}
|
||
|
||
// 检查文件大小(限制10MB)
|
||
const isLt10M = file.size / 1024 / 1024 < 10;
|
||
if (!isLt10M) {
|
||
this.$message.error('图片大小不能超过10MB');
|
||
return false;
|
||
}
|
||
|
||
console.log('弹窗图片检查通过,开始上传...');
|
||
this.$message.info('图片上传中...');
|
||
|
||
return true;
|
||
},
|
||
|
||
// 弹窗图片上传成功
|
||
handlePopupImgSuccess(res, file, index) {
|
||
console.log('弹窗图片上传成功回调:', res, file, index);
|
||
console.log('完整响应数据:', JSON.stringify(res, null, 2));
|
||
|
||
// 处理上传成功的响应
|
||
let url = '';
|
||
if (res && res.code === 200) {
|
||
// 标准的RuoYi响应格式
|
||
if (res.url) {
|
||
url = res.url;
|
||
} else if (res.fileName) {
|
||
url = res.fileName;
|
||
} else if (res.data && res.data.url) {
|
||
url = res.data.url;
|
||
} else if (res.data && res.data.fileName) {
|
||
url = res.data.fileName;
|
||
}
|
||
} else if (res) {
|
||
// 兼容其他格式
|
||
if (typeof res === 'string') {
|
||
url = res;
|
||
} else if (res.url) {
|
||
url = res.url;
|
||
} else if (res.fileName) {
|
||
url = res.fileName;
|
||
} else if (res.data) {
|
||
if (typeof res.data === 'string') {
|
||
url = res.data;
|
||
} else if (res.data.url) {
|
||
url = res.data.url;
|
||
} else if (res.data.fileName) {
|
||
url = res.data.fileName;
|
||
}
|
||
}
|
||
}
|
||
|
||
console.log('解析出的弹窗图片URL:', url);
|
||
|
||
// 如果是相对路径,自动拼接完整URL
|
||
if (url && !/^https?:\/\//.test(url)) {
|
||
// 使用VUE_APP_BASE_API或当前域名
|
||
const base = process.env.VUE_APP_BASE_API || window.location.origin;
|
||
url = base.replace(/\/$/, '') + (url.startsWith('/') ? url : '/' + url);
|
||
}
|
||
|
||
console.log('最终处理后的弹窗图片URL:', url);
|
||
|
||
if (url) {
|
||
// 确保索引有效
|
||
if (this.popupForm.popups[index]) {
|
||
this.$set(this.popupForm.popups[index], 'imgUrl', url);
|
||
this.$message.success('弹窗图片上传成功');
|
||
console.log('弹窗图片URL已设置到弹窗数据:', this.popupForm.popups[index]);
|
||
|
||
// 强制刷新上传组件
|
||
this.$forceUpdate();
|
||
} else {
|
||
console.error('弹窗索引无效:', index);
|
||
this.$message.error('弹窗索引无效');
|
||
}
|
||
} else {
|
||
console.error('无法从响应中获取弹窗图片URL');
|
||
console.error('响应数据结构:', res);
|
||
this.$message.error('弹窗图片上传失败:无法获取图片地址');
|
||
}
|
||
},
|
||
|
||
// 弹窗图片上传失败
|
||
handlePopupImgError(err, file, index) {
|
||
console.error('弹窗图片上传失败:', err, file, index);
|
||
console.error('错误详情:', JSON.stringify(err, null, 2));
|
||
|
||
let errorMessage = '弹窗图片上传失败';
|
||
if (err.message) {
|
||
errorMessage += ':' + err.message;
|
||
} else if (err.response && err.response.data && err.response.data.msg) {
|
||
errorMessage += ':' + err.response.data.msg;
|
||
} else if (err.response && err.response.statusText) {
|
||
errorMessage += ':' + err.response.statusText;
|
||
} else {
|
||
errorMessage += ':网络错误';
|
||
}
|
||
|
||
this.$message.error(errorMessage);
|
||
},
|
||
|
||
// 删除弹窗图片
|
||
removePopupImg(index) {
|
||
this.$confirm('确定要删除这张弹窗图片吗?', '删除确认', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.$set(this.popupForm.popups[index], 'imgUrl', '');
|
||
this.$message.success('弹窗图片删除成功');
|
||
}).catch(() => {
|
||
this.$message.info('已取消删除');
|
||
});
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.sysseting-container {
|
||
background: #fff;
|
||
padding: 24px;
|
||
min-height: 600px;
|
||
}
|
||
.tab-form {
|
||
max-width: 900px;
|
||
}
|
||
.qr-img {
|
||
width: 120px;
|
||
height: 120px;
|
||
border: 1px solid #eee;
|
||
display: block;
|
||
}
|
||
.avatar-uploader .el-upload {
|
||
border: 1px dashed #d9d9d9;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
width: 120px;
|
||
height: 120px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.avatar-uploader-icon {
|
||
font-size: 32px;
|
||
color: #8c939d;
|
||
}
|
||
|
||
/* 通知公告容器样式 */
|
||
.notice-container {
|
||
background: #f8f9fa;
|
||
min-height: 600px;
|
||
padding: 0;
|
||
}
|
||
|
||
/* 页面头部样式 */
|
||
.notice-header {
|
||
background: #fff;
|
||
padding: 24px;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.notice-title h3 {
|
||
margin: 0 0 8px 0;
|
||
color: #303133;
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.notice-desc {
|
||
margin: 0;
|
||
color: #909399;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.notice-header-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.notice-filter {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
/* 空状态样式 */
|
||
.notice-empty {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 40px;
|
||
text-align: center;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
/* 公告列表样式 */
|
||
.notice-list {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.notice-item {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
/* 公告卡片样式 */
|
||
.notice-card {
|
||
border-radius: 8px;
|
||
border: 1px solid #e4e7ed;
|
||
transition: all 0.3s ease;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.notice-card:hover {
|
||
border-color: #409EFF;
|
||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
||
}
|
||
|
||
.notice-card.notice-offline {
|
||
background: #fafafa;
|
||
border-color: #f0f0f0;
|
||
}
|
||
|
||
.notice-card.notice-offline:hover {
|
||
border-color: #d9d9d9;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.notice-card.notice-expanded {
|
||
border-color: #409EFF;
|
||
}
|
||
|
||
/* 简洁行显示样式 */
|
||
.notice-simple-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s ease;
|
||
min-height: 60px;
|
||
}
|
||
|
||
.notice-simple-row:hover {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.notice-simple-left {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.notice-simple-info {
|
||
margin-left: 12px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.notice-simple-title {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.notice-category-tag-left {
|
||
margin-left: 12px;
|
||
margin-right: 8px;
|
||
flex-shrink: 0;
|
||
align-self: flex-start;
|
||
}
|
||
|
||
.notice-simple-meta {
|
||
display: block;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
line-height: 1.2;
|
||
}
|
||
|
||
.notice-simple-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.notice-expand-icon {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
transition: transform 0.3s ease;
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.notice-card.notice-expanded .notice-expand-icon {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
/* 序号样式 */
|
||
.notice-index {
|
||
background: #409EFF;
|
||
color: #fff;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* 卡片内容样式 */
|
||
.notice-card-content {
|
||
padding: 0 20px 20px 20px;
|
||
background: #fafbfc;
|
||
}
|
||
|
||
/* 编辑区域底部操作按钮 */
|
||
.notice-edit-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
margin-top: 20px;
|
||
padding-top: 16px;
|
||
border-top: 1px solid #ebeef5;
|
||
}
|
||
|
||
.notice-form-grid {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20px;
|
||
}
|
||
|
||
.notice-form-row.full-width {
|
||
width: 100%;
|
||
}
|
||
|
||
.notice-form-row.two-columns {
|
||
display: grid;
|
||
grid-template-columns: 2fr 1fr;
|
||
gap: 20px;
|
||
align-items: start;
|
||
}
|
||
|
||
.notice-title-item .el-form-item__content {
|
||
width: 100%;
|
||
}
|
||
|
||
.notice-content-item .el-form-item__content {
|
||
width: 100%;
|
||
}
|
||
|
||
.notice-link-item .el-form-item__content {
|
||
width: 100%;
|
||
}
|
||
|
||
.notice-sort-item .el-form-item__content {
|
||
width: 100%;
|
||
}
|
||
|
||
/* 富文本编辑器样式 */
|
||
.notice-editor {
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.notice-editor .ql-toolbar {
|
||
border-top: 1px solid #e4e7ed;
|
||
border-left: 1px solid #e4e7ed;
|
||
border-right: 1px solid #e4e7ed;
|
||
background: #fafbfc;
|
||
}
|
||
|
||
.notice-editor .ql-container {
|
||
border-bottom: 1px solid #e4e7ed;
|
||
border-left: 1px solid #e4e7ed;
|
||
border-right: 1px solid #e4e7ed;
|
||
min-height: 200px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.notice-editor .ql-editor {
|
||
padding: 16px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* 底部操作区样式 */
|
||
.notice-footer {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 24px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.notice-footer-actions {
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 12px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.notice-footer-tips {
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.notice-footer-tips .el-alert {
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.notice-footer-tips .el-alert__description p {
|
||
margin: 4px 0;
|
||
color: #606266;
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* 活动专区维护容器样式 */
|
||
.activity-zone-container { background: #f8f9fa; min-height: 400px; padding: 0; }
|
||
.activity-header { background: #fff; padding: 24px; border-radius: 8px; margin-bottom: 20px; display: flex; gap: 12px; }
|
||
.activity-card { margin-bottom: 20px; }
|
||
.activity-img { width: 120px; height: 120px; border: 1px solid #eee; display: block; }
|
||
.activity-uploader-icon { font-size: 32px; color: #8c939d; }
|
||
.activity-actions { display: flex; justify-content: flex-end; margin-top: 12px; }
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 768px) {
|
||
.notice-header {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 16px;
|
||
}
|
||
|
||
.notice-header-actions {
|
||
width: 100%;
|
||
justify-content: flex-start;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.notice-filter {
|
||
order: -1;
|
||
width: 100%;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.notice-simple-row {
|
||
padding: 12px 16px;
|
||
min-height: 50px;
|
||
}
|
||
|
||
.notice-simple-left {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
|
||
.notice-simple-info {
|
||
margin-left: 0;
|
||
margin-top: 0;
|
||
}
|
||
|
||
.notice-category-tag-left {
|
||
margin-left: 0;
|
||
margin-right: 0;
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.notice-simple-title {
|
||
font-size: 13px;
|
||
}
|
||
|
||
.notice-simple-right {
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.notice-card-content {
|
||
padding: 0 16px 16px 16px;
|
||
}
|
||
|
||
.notice-form-row.two-columns {
|
||
grid-template-columns: 1fr;
|
||
gap: 16px;
|
||
}
|
||
|
||
.notice-edit-actions {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.notice-footer-actions {
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
}
|
||
|
||
.activity-simple-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s ease;
|
||
min-height: 60px;
|
||
}
|
||
.activity-simple-row:hover {
|
||
background-color: #f8f9fa;
|
||
}
|
||
.activity-simple-left {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
.activity-simple-info {
|
||
margin-left: 12px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
.activity-simple-title {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.activity-simple-content {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.activity-thumb {
|
||
width: 48px;
|
||
height: 48px;
|
||
object-fit: cover;
|
||
border-radius: 4px;
|
||
margin-left: 16px;
|
||
border: 1px solid #eee;
|
||
}
|
||
.activity-simple-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
.activity-expand-icon {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
transition: transform 0.3s ease;
|
||
margin-left: 8px;
|
||
}
|
||
.activity-card.activity-expanded .activity-expand-icon {
|
||
transform: rotate(180deg);
|
||
}
|
||
.activity-index {
|
||
background: #409EFF;
|
||
color: #fff;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
}
|
||
.activity-card-content {
|
||
padding: 0 20px 20px 20px;
|
||
background: #fafbfc;
|
||
}
|
||
.activity-edit-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
margin-top: 20px;
|
||
padding-top: 16px;
|
||
border-top: 1px solid #ebeef5;
|
||
}
|
||
.activity-img-delete-btn {
|
||
position: absolute;
|
||
top: 2px;
|
||
right: 2px;
|
||
z-index: 10;
|
||
padding: 0;
|
||
width: 22px;
|
||
height: 22px;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
line-height: 22px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.activity-simple-link {
|
||
font-size: 11px;
|
||
color: #409EFF;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
margin-top: 2px;
|
||
}
|
||
.activity-simple-position {
|
||
font-size: 11px;
|
||
color: #67C23A;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* 首页弹窗维护容器样式 */
|
||
.popup-container {
|
||
background: #f8f9fa;
|
||
min-height: 400px;
|
||
padding: 0;
|
||
}
|
||
|
||
.popup-header {
|
||
background: #fff;
|
||
padding: 24px;
|
||
border-radius: 8px;
|
||
margin-bottom: 20px;
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.popup-empty {
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
padding: 40px;
|
||
text-align: center;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.popup-item {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.popup-card {
|
||
border-radius: 8px;
|
||
border: 1px solid #e4e7ed;
|
||
transition: all 0.3s ease;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.popup-card:hover {
|
||
border-color: #409EFF;
|
||
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15);
|
||
}
|
||
|
||
.popup-card.popup-offline {
|
||
background: #fafafa;
|
||
border-color: #f0f0f0;
|
||
}
|
||
|
||
.popup-card.popup-offline:hover {
|
||
border-color: #d9d9d9;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
}
|
||
|
||
.popup-card.popup-expanded {
|
||
border-color: #409EFF;
|
||
}
|
||
|
||
.popup-simple-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 16px 20px;
|
||
cursor: pointer;
|
||
transition: background-color 0.2s ease;
|
||
min-height: 60px;
|
||
}
|
||
|
||
.popup-simple-row:hover {
|
||
background-color: #f8f9fa;
|
||
}
|
||
|
||
.popup-simple-left {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.popup-simple-info {
|
||
margin-left: 12px;
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.popup-simple-title {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
color: #303133;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.popup-simple-link {
|
||
font-size: 11px;
|
||
color: #409EFF;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.popup-simple-position {
|
||
font-size: 11px;
|
||
color: #67C23A;
|
||
line-height: 1.2;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.popup-thumb {
|
||
width: 48px;
|
||
height: 48px;
|
||
object-fit: cover;
|
||
border-radius: 4px;
|
||
margin-left: 16px;
|
||
border: 1px solid #eee;
|
||
}
|
||
|
||
.popup-simple-right {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.popup-expand-icon {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
transition: transform 0.3s ease;
|
||
margin-left: 8px;
|
||
}
|
||
|
||
.popup-card.popup-expanded .popup-expand-icon {
|
||
transform: rotate(180deg);
|
||
}
|
||
|
||
.popup-index {
|
||
background: #409EFF;
|
||
color: #fff;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.popup-card-content {
|
||
padding: 0 20px 20px 20px;
|
||
background: #fafbfc;
|
||
}
|
||
|
||
.popup-edit-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
margin-top: 20px;
|
||
padding-top: 16px;
|
||
border-top: 1px solid #ebeef5;
|
||
}
|
||
|
||
.popup-img {
|
||
width: 120px;
|
||
height: 120px;
|
||
border: 1px solid #eee;
|
||
display: block;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.popup-uploader-icon {
|
||
font-size: 32px;
|
||
color: #8c939d;
|
||
}
|
||
|
||
.popup-img-uploader .el-upload {
|
||
border: 1px dashed #d9d9d9;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
position: relative;
|
||
overflow: hidden;
|
||
width: 120px;
|
||
height: 120px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.popup-img-delete-btn {
|
||
position: absolute;
|
||
top: 2px;
|
||
right: 2px;
|
||
z-index: 10;
|
||
padding: 0;
|
||
width: 22px;
|
||
height: 22px;
|
||
min-width: 0;
|
||
min-height: 0;
|
||
line-height: 22px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
</style>
|