Kaynağa Gözat

报表查询/信息公告/平台设置

pxb 15 saat önce
ebeveyn
işleme
9ed4bce127

+ 1 - 0
src/components/Dialog/index.vue

@@ -3,6 +3,7 @@
     :visible.sync="visible"
     :width=" actions.width"
     :modal="actions.modal"
+    :class="actions.class"
     v-bind="$attrs"
     v-on="$listeners"
   >

+ 72 - 0
src/router/index.js

@@ -701,6 +701,78 @@ export const constantRoutes = [
   //     }
   //   ]
   // },
+  {
+    path: "/InformationAnnouncement",
+    name: "InformationAnnouncement",
+    component: Layout,
+    redirect: "/InformationAnnouncement/InfoManagement",
+    meta: { title: "信息公告", icon: "form" },
+    children: [
+      {
+        path: "InfoManagement",
+        component: () =>
+          import("@/views/InformationAnnouncement/InfoManagement/index"),
+        name: "InfoManagement",
+        meta: { title: "信息管理" },
+      },
+      {
+        path: "AnnouncementManagement",
+        component: () =>
+          import("@/views/InformationAnnouncement/AnnouncementManagement/index"),
+        name: "AnnouncementManagement",
+        meta: { title: "公告管理" },
+      },
+    ],
+  },
+  {
+    path: "/ReportQuery",
+    name: "ReportQuery",
+    component: Layout,
+    redirect: "/ReportQuery/ReportSummary",
+    meta: { title: "报表查询", icon: "chart" },
+    children: [
+      {
+        path: "ReportSummary",
+        component: () => import("@/views/ReportQuery/ReportSummary/index"),
+        name: "ReportSummary",
+        meta: { title: "报表汇总" },
+      },
+      {
+        path: "CapitalFlow",
+        component: () => import("@/views/ReportQuery/CapitalFlow/index"),
+        name: "CapitalFlow",
+        meta: { title: "资金流动" },
+      },
+      {
+        path: "RankingList",
+        component: () => import("@/views/ReportQuery/RankingList/index"),
+        name: "RankingList",
+        meta: { title: "龙虎榜" },
+      },
+    ],
+  },
+  {
+    path: "/PlatformSetting",
+    name: "PlatformSetting",
+    component: Layout,
+    redirect: "/PlatformSetting/MaintenanceSetting",
+    meta: { title: "平台设置", icon: "setting" },
+    children: [
+      {
+        path: "MaintenanceSetting",
+        component: () =>
+          import("@/views/PlatformSetting/MaintenanceSetting/index"),
+        name: "MaintenanceSetting",
+        meta: { title: "维护设定" },
+      },
+      {
+        path: "MallSetting",
+        component: () => import("@/views/PlatformSetting/MallSetting/index"),
+        name: "MallSetting",
+        meta: { title: "商城设定" },
+      },
+    ],
+  },
   // 404 page must be placed at the end !!!
   { path: "*", redirect: "/404", hidden: true },
 ];

+ 4 - 0
src/styles/element-ui.scss

@@ -228,3 +228,7 @@ input[type="number"]{
     border-color: #3E7BFA;
   }
 }
+/* 查看类弹窗:避免内容区出现横向滚动条 */
+.el-dialog__wrapper.is-detail-dialog .el-dialog__body {
+  overflow-x: hidden;
+}

+ 277 - 0
src/views/InformationAnnouncement/AnnouncementManagement/index.vue

@@ -0,0 +1,277 @@
+<template>
+  <div class="new-app-container layout-vertical operation-setting-page">
+    <div class="operation-setting-panel">
+      <el-tabs v-model="activeTab" class="operation-tabs">
+        <el-tab-pane label="公告管理" name="list" />
+        <el-tab-pane label="公告日记" name="diary" />
+      </el-tabs>
+
+      <template v-if="activeTab === 'list'">
+        <el-form :model="list.query" class="operation-search" inline size="small" @submit.native.prevent="searchList">
+          <el-form-item label="公告分类"><el-select v-model="list.query.category" placeholder="请选择" clearable><el-option v-for="item in categoryOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
+          <el-form-item label="发布平台"><el-select v-model="list.query.platform" placeholder="请选择" clearable><el-option label="全部" value="全部" /><el-option label="H5" value="H5" /><el-option label="App" value="App" /></el-select></el-form-item>
+          <el-form-item label="状态"><el-select v-model="list.query.status" placeholder="请选择" clearable><el-option label="启用" :value="1" /><el-option label="关闭" :value="0" /></el-select></el-form-item>
+          <el-form-item label="时间"><el-date-picker v-model="list.query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+          <el-form-item label="公告标题"><el-input v-model="list.query.title" placeholder="搜索公告标题" clearable /></el-form-item>
+          <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="searchList">查询</el-button><el-button @click="resetList">重置</el-button></el-form-item>
+        </el-form>
+
+        <div class="page-notes">
+          <p v-for="(note, index) in listNotes" :key="index">{{ note }}</p>
+        </div>
+
+        <div class="operation-toolbar"><el-button type="primary" size="small" @click="createAnnouncement">新增</el-button></div>
+
+        <div class="operation-table-wrap"><el-table v-loading="loading" :data="list.rows" height="100%" class="operation-table">
+          <el-table-column label="序号" width="70" align="center"><template slot-scope="scope">{{ (list.page - 1) * list.limit + scope.$index + 1 }}</template></el-table-column>
+          <el-table-column prop="announcement_type" label="公告类型" min-width="120" align="center" />
+          <el-table-column prop="title" label="公告标题" min-width="220" align="center" />
+          <el-table-column prop="platform" label="发布平台" min-width="110" align="center" />
+          <el-table-column prop="frequency" label="展示频率" min-width="110" align="center" />
+          <el-table-column prop="send_time" label="发送时间" min-width="180" align="center" />
+          <el-table-column prop="end_time" label="结束时间" min-width="180" align="center" />
+          <el-table-column label="发送者" min-width="150" align="center"><template slot-scope="scope"><span class="sender-cell">批核者:<span class="sender-name">{{ scope.row.sender }}</span></span></template></el-table-column>
+          <el-table-column label="功能" width="150" align="center" fixed="right"><template slot-scope="scope"><el-button type="text" size="small" class="operation-link" @click="viewAnnouncement(scope.row)">查看</el-button><el-button type="text" size="small" class="operation-link danger-link" @click="toggleAnnouncement(scope.row)">{{ scope.row.status === 1 ? '关闭' : '开启' }}</el-button></template></el-table-column>
+        </el-table></div>
+        <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="list.page" :page-size="list.limit" :page-sizes="pageSizes" :total="list.total" @size-change="changePageSize('list', $event)" @current-change="changePage('list', $event)" />
+      </template>
+
+      <template v-else>
+        <el-form :model="diary.query" class="operation-search" inline size="small" @submit.native.prevent="searchDiary">
+          <el-form-item label="时间"><el-date-picker v-model="diary.query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+          <el-form-item label="操作员账号"><el-input v-model="diary.query.operator" placeholder="搜索操作员账号" clearable /></el-form-item>
+          <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="searchDiary">查询</el-button><el-button @click="resetDiary">重置</el-button></el-form-item>
+        </el-form>
+
+        <div class="operation-table-wrap"><el-table v-loading="loading" :data="diary.rows" height="100%" class="operation-table">
+          <el-table-column prop="date" label="日期" min-width="180" align="center" />
+          <el-table-column prop="operator" label="操作员账号" min-width="140" align="center" />
+          <el-table-column prop="item" label="操作项目" min-width="140" align="center" />
+          <el-table-column prop="target" label="发送对象" min-width="130" align="center" />
+          <el-table-column prop="old_value" label="旧值" min-width="120" align="center" />
+          <el-table-column prop="new_value" label="新值" min-width="120" align="center" />
+        </el-table></div>
+        <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="diary.page" :page-size="diary.limit" :page-sizes="pageSizes" :total="diary.total" @size-change="changePageSize('diary', $event)" @current-change="changePage('diary', $event)" />
+      </template>
+    </div>
+
+    <Dialog ref="createDialog" :dialog-actions="{ width: '880px', title: '新增公告', buttom: false, confirmButtom: false }">
+      <template slot="content">
+        <el-form :model="form" label-width="120px" class="announcement-dialog-form">
+          <el-form-item label="公告标题"><el-input v-model="form.title" placeholder="请输入公告标题" /></el-form-item>
+          <el-form-item label="公告分类"><el-select v-model="form.category" placeholder="请选择"><el-option v-for="item in categoryOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
+          <el-form-item label="发布时间"><el-date-picker v-model="form.send_time" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择发布时间" /><span class="inline-label">即使发布</span><el-switch v-model="form.publish_now" class="inline-switch" /></el-form-item>
+          <el-form-item label="结束时间"><el-date-picker v-model="form.end_time" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择结束时间" /><span class="inline-label">不需要结束</span><el-switch v-model="form.never_end" class="inline-switch" /></el-form-item>
+          <el-form-item label="发布平台"><el-checkbox-group v-model="form.platforms"><el-checkbox label="H5">H5</el-checkbox><el-checkbox label="App">App</el-checkbox></el-checkbox-group></el-form-item>
+          <el-form-item label="展示频率"><el-radio-group v-model="form.frequency"><el-radio label="每日首次登录">每日首次登录</el-radio><el-radio label="每次登录">每次登录</el-radio></el-radio-group></el-form-item>
+
+          <div class="form-section-title">公告设定</div>
+          <el-form-item label="图示点击行为"><el-radio-group v-model="form.click_action"><el-radio label="无链接">无链接</el-radio><el-radio label="外站链接">外站链接</el-radio><el-radio label="站内相对路径">站内相对路径</el-radio><el-radio label="优惠活动">优惠活动</el-radio></el-radio-group></el-form-item>
+
+          <el-form-item v-if="form.category === '弹出图片公告'" label="图片公告"><div class="upload-placeholder" :class="{ 'has-image': form.image }" @click="pickImage">
+            <template v-if="form.image">
+              <img :src="form.image" class="upload-thumb" alt="图片公告">
+              <span class="upload-replace">点击更换图片</span>
+            </template>
+            <template v-else>
+              <span class="upload-plus">+</span>
+              <p>网页版图片建议 315 × 300</p>
+              <p>及(大于等于)同等比例, 300KB内</p>
+            </template>
+          </div><input ref="imageInput" type="file" accept="image/*" class="hidden-file" @change="onImageChange"></el-form-item>
+
+          <el-form-item v-else label="内容公告"><el-input v-model="form.content" type="textarea" :rows="8" placeholder="请输入内容" /></el-form-item>
+
+          <el-form-item label="状态"><el-switch v-model="form.status" :active-value="1" :inactive-value="0" /></el-form-item>
+          <div class="dialog-actions"><el-button type="primary" @click="submitAnnouncement">提交</el-button></div>
+        </el-form>
+      </template>
+    </Dialog>
+
+    <Dialog ref="viewDialog" :dialog-actions="{ width: '720px', class: 'is-detail-dialog', title: '查看公告', confirmButtom: false, cancelButtom: true, buttonTextLeft: '关闭' }">
+      <template slot="content">
+        <el-form label-width="110px" class="announcement-detail-form">
+          <el-form-item label="公告类型"><span class="detail-text">{{ detail.announcement_type }}</span></el-form-item>
+          <el-form-item label="公告标题"><span class="detail-text">{{ detail.title }}</span></el-form-item>
+          <el-form-item label="发布平台"><span class="detail-text">{{ detail.platform }}</span></el-form-item>
+          <el-form-item label="展示频率"><span class="detail-text">{{ detail.frequency }}</span></el-form-item>
+          <el-form-item label="发送时间"><span class="detail-text">{{ detail.send_time }}</span></el-form-item>
+          <el-form-item label="结束时间"><span class="detail-text">{{ detail.end_time }}</span></el-form-item>
+          <el-form-item label="发送者"><span class="detail-text">{{ detail.sender }}</span></el-form-item>
+          <el-form-item label="状态"><span class="detail-text">{{ detail.status === 1 ? '启用' : '关闭' }}</span></el-form-item>
+          <el-form-item label="公告内容"><div class="detail-content">{{ detail.content }}</div></el-form-item>
+        </el-form>
+      </template>
+    </Dialog>
+  </div>
+</template>
+
+<script>
+import Dialog from '@/components/Dialog/index.vue'
+import { disableFutureDate } from '@/utils/index'
+
+const CATEGORY_OPTIONS = [
+  { label: '弹出内容公告', value: '弹出内容公告' },
+  { label: '弹出图片公告', value: '弹出图片公告' },
+  { label: '弹出更新公告', value: '弹出更新公告' }
+]
+
+export default {
+  name: 'AnnouncementManagement',
+  components: { Dialog },
+  data() {
+    return {
+      disableFutureDate,
+      pageSizes: [10, 20, 50, 100],
+      loading: false,
+      activeTab: 'list',
+      categoryOptions: CATEGORY_OPTIONS,
+      listNotes: ['公告分类:弹出图片公告 弹出内容公告 弹出更新公告'],
+      list: { page: 1, limit: 20, total: 0, query: { category: '', platform: '', status: '', date_range: [], title: '' }, rows: [] },
+      diary: { page: 1, limit: 20, total: 0, query: { date_range: [], operator: '' }, rows: [] },
+      form: {},
+      detail: {}
+    }
+  },
+  created() {
+    this.loadList()
+    this.loadDiary()
+  },
+  methods: {
+    newForm() {
+      return { title: '', category: '弹出内容公告', send_time: '', publish_now: true, end_time: '', never_end: true, platforms: ['H5', 'App'], frequency: '每日首次登录', click_action: '无链接', content: '', image: '', status: 1 }
+    },
+    pickImage() { this.$refs.imageInput.click() },
+    onImageChange(event) {
+      const file = event.target.files && event.target.files[0]
+      if (!file) return
+      if (file.size > 300 * 1024) { this.$message.warning('图片大小不能超过 300KB'); event.target.value = ''; return }
+      const reader = new FileReader()
+      reader.onload = () => { this.form.image = reader.result }
+      reader.readAsDataURL(file)
+      event.target.value = ''
+    },
+    mockList() {
+      const base = [
+        { announcement_type: '网站公告', title: '网站维护', category: '弹出内容公告', frequency: '每次登录' },
+        { announcement_type: '图片公告', title: '新年活动预告', category: '弹出图片公告', frequency: '每日首次登录' },
+        { announcement_type: '更新公告', title: '版本更新说明', category: '弹出更新公告', frequency: '每日首次登录' }
+      ]
+      return Array.from({ length: 19 }, (_, index) => {
+        const item = base[index % base.length]
+        const day = String((index % 9) + 1).padStart(2, '0')
+        return {
+          id: index + 1,
+          announcement_type: item.announcement_type,
+          title: item.title,
+          category: item.category,
+          platform: index % 3 === 0 ? '全部' : (index % 3 === 1 ? 'H5' : 'App'),
+          frequency: item.frequency,
+          send_time: `2025-03-${day} 12:12:09`,
+          end_time: `2025-03-${day} 23:59:59`,
+          sender: 'admin',
+          status: index % 4 === 0 ? 0 : 1,
+          content: `这是「${item.title}」的公告内容示例,用于验证页面排版与查看弹窗的展示效果。`
+        }
+      })
+    },
+    mockDiaryRows() {
+      const source = [
+        { item: '网站维护', target: '所有人', old_value: '修改', new_value: '启用' },
+        { item: '网站维护', target: '所有人', old_value: '启用', new_value: '修改' },
+        { item: '版本更新说明', target: '所有人', old_value: '关闭', new_value: '启用' }
+      ]
+      return Array.from({ length: 14 }, (_, index) => {
+        const row = source[index % source.length]
+        const day = String((index % 9) + 1).padStart(2, '0')
+        return {
+          id: index + 1,
+          date: `2025-03-${day} 22:${String(index % 60).padStart(2, '0')}:43`,
+          operator: 'hope123',
+          item: row.item,
+          target: row.target,
+          old_value: row.old_value,
+          new_value: row.new_value
+        }
+      })
+    },
+    loadList() {
+      this.loading = true
+      const query = this.list.query
+      const [start_date, end_date] = query.date_range || []
+      const rows = this.mockList().filter(row => {
+        const day = row.send_time.slice(0, 10)
+        if (query.category && row.category !== query.category) return false
+        if (query.platform && row.platform !== query.platform) return false
+        if (query.status !== '' && row.status !== query.status) return false
+        if (query.title && !row.title.includes(query.title)) return false
+        if (start_date && day < start_date) return false
+        if (end_date && day > end_date) return false
+        return true
+      })
+      const start = (this.list.page - 1) * this.list.limit
+      this.list.total = rows.length
+      this.list.rows = rows.slice(start, start + this.list.limit)
+      this.loading = false
+    },
+    loadDiary() {
+      const [start_date, end_date] = this.diary.query.date_range || []
+      const rows = this.mockDiaryRows().filter(row => {
+        const day = row.date.slice(0, 10)
+        if (start_date && day < start_date) return false
+        if (end_date && day > end_date) return false
+        return !this.diary.query.operator || row.operator.includes(this.diary.query.operator)
+      })
+      const start = (this.diary.page - 1) * this.diary.limit
+      this.diary.total = rows.length
+      this.diary.rows = rows.slice(start, start + this.diary.limit)
+    },
+    searchList() { this.list.page = 1; this.loadList() },
+    resetList() { this.list.query = { category: '', platform: '', status: '', date_range: [], title: '' }; this.list.page = 1; this.loadList() },
+    searchDiary() { this.diary.page = 1; this.loadDiary() },
+    resetDiary() { this.diary.query = { date_range: [], operator: '' }; this.diary.page = 1; this.loadDiary() },
+    changePage(key, page) { this[key].page = page; key === 'list' ? this.loadList() : this.loadDiary() },
+    changePageSize(key, limit) { this[key].limit = limit; this[key].page = 1; key === 'list' ? this.loadList() : this.loadDiary() },
+    createAnnouncement() { this.form = this.newForm(); this.$refs.createDialog.open() },
+    viewAnnouncement(row) { this.detail = row; this.$refs.viewDialog.open() },
+    toggleAnnouncement(row) {
+      const turningOn = row.status !== 1
+      this.$confirm(`确定${turningOn ? '开启' : '关闭'}这条公告吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
+        .then(() => {
+          row.status = turningOn ? 1 : 0
+          this.$message.success(turningOn ? '已开启' : '已关闭')
+        })
+        .catch(() => {})
+    },
+    submitAnnouncement() {
+      if (!this.form.title) { this.$message.warning('请输入公告标题'); return }
+      this.$refs.createDialog.close()
+      this.$message.success('提交成功')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.operation-setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.operation-setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; padding: 0 20px 12px; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.operation-tabs { flex-shrink: 0; ::v-deep .el-tabs__header { margin: 0; } ::v-deep .el-tabs__item { height: 52px; line-height: 52px; font-weight: 500; } }
+.operation-search { display: flex; flex-wrap: wrap; align-items: center; flex-shrink: 0; padding-top: 16px; ::v-deep .el-form-item { display: flex; flex: 0 0 auto; align-items: center; margin-right: 28px !important; margin-bottom: 14px; white-space: nowrap; } ::v-deep .el-form-item__label { float: none; width: auto !important; padding: 0 8px 0 0; color: #303133; font-weight: 500; line-height: 32px; } ::v-deep .el-form-item__content { display: flex; align-items: center; margin-left: 0 !important; line-height: 32px; } ::v-deep .el-input { width: 180px; } ::v-deep .el-select { width: 180px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-date-editor--daterange.el-input__inner { width: 300px; } .search-actions { margin-right: 0 !important; } }
+.operation-toolbar { display: flex; flex-shrink: 0; align-items: center; min-height: 42px; padding-bottom: 10px; }
+.operation-toolbar-title { color: #303133; font-size: 15px; font-weight: 500; }
+.operation-table-wrap { display: flex; flex: 1; min-height: 0; flex-direction: column; }
+.operation-table { flex: 1; ::v-deep th { height: 44px; background: #f5f7fa; } ::v-deep th .cell { color: #606266 !important; font-weight: 500; } ::v-deep td { height: 49px; } ::v-deep td .cell { color: #606266; font-size: 14px; } ::v-deep .el-table__fixed-right { height: 100% !important; } }
+.operation-link { margin: 0 4px; color: #3e7bfa; } .danger-link { color: #f56c6c; }
+.operation-pagination { flex-shrink: 0; padding-top: 12px; text-align: right; }
+.sender-cell { white-space: nowrap; } .sender-name { color: #3e7bfa; }
+.page-notes { flex-shrink: 0; padding: 16px 0 2px; p { margin: 0 0 2px; color: #f56c6c; font-size: 13px; line-height: 22px; } }
+.announcement-dialog-form { max-height: 580px; overflow-y: auto; padding: 12px 20px 0; ::v-deep .el-form-item { margin-bottom: 16px; } ::v-deep .el-input { width: 320px; } ::v-deep .el-select { width: 240px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-textarea { width: 440px; } .inline-label { margin-left: 12px; color: #606266; } .inline-switch { margin-left: 6px; } .form-section-title { margin: 4px 0 16px 8px; color: #303133; font-size: 16px; font-weight: 600; } }
+.upload-placeholder { display: flex; flex-direction: column; justify-content: center; align-items: center; width: 300px; height: 190px; background: #eef0f4; border: 1px dashed #d9dce0; border-radius: 4px; color: #909399; cursor: pointer; overflow: hidden; p { margin: 2px 0; font-size: 13px; } .upload-plus { margin-bottom: 2px; color: #c0c4cc; font-size: 30px; line-height: 1; } &.has-image { position: relative; padding: 0; border-style: solid; } }
+.upload-thumb { width: 100%; height: 100%; object-fit: contain; }
+.upload-replace { position: absolute; bottom: 0; width: 100%; padding: 2px 0; background: rgba(0, 0, 0, 0.45); color: #fff; font-size: 12px; text-align: center; }
+.hidden-file { display: none; }
+.announcement-detail-form { padding: 4px 12px 0; ::v-deep .el-form-item { margin-bottom: 12px; } ::v-deep .el-form-item__label { line-height: 22px; } ::v-deep .el-form-item__content { color: #606266; line-height: 22px; } }
+.detail-text { color: #606266; line-height: 22px; word-break: break-all; }
+.detail-content { width: 100%; padding: 10px 12px; background: #f5f7fa; border-radius: 4px; line-height: 22px; white-space: pre-wrap; }
+.dialog-actions { padding: 8px 0 4px; text-align: center; }
+</style>

+ 239 - 0
src/views/InformationAnnouncement/InfoManagement/index.vue

@@ -0,0 +1,239 @@
+<template>
+  <div class="new-app-container layout-vertical operation-setting-page">
+    <div class="operation-setting-panel">
+      <el-tabs v-model="activeTab" class="operation-tabs">
+        <el-tab-pane label="信息管理" name="list" />
+        <el-tab-pane label="信息日记" name="diary" />
+      </el-tabs>
+
+      <template v-if="activeTab === 'list'">
+        <el-form :model="list.query" class="operation-search" inline size="small" @submit.native.prevent="searchList">
+          <el-form-item label="信息分类"><el-select v-model="list.query.category" placeholder="请选择" clearable><el-option v-for="item in categoryOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
+          <el-form-item label="时间"><el-date-picker v-model="list.query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+          <el-form-item label="信息标题"><el-input v-model="list.query.title" placeholder="搜索信息标题" clearable /></el-form-item>
+          <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="searchList">查询</el-button><el-button @click="resetList">重置</el-button></el-form-item>
+        </el-form>
+
+        <div class="page-notes">
+          <p v-for="(note, index) in listNotes" :key="index">{{ note }}</p>
+        </div>
+
+        <div class="operation-toolbar"><el-button type="primary" size="small" @click="createInfo">新增</el-button></div>
+
+        <div class="operation-table-wrap"><el-table v-loading="loading" :data="list.rows" height="100%" class="operation-table">
+          <el-table-column label="序号" width="70" align="center"><template slot-scope="scope">{{ (list.page - 1) * list.limit + scope.$index + 1 }}</template></el-table-column>
+          <el-table-column prop="info_type" label="信息类型" min-width="120" align="center" />
+          <el-table-column prop="title" label="信息标题" min-width="220" align="center" />
+          <el-table-column prop="receiver" label="收件对象" min-width="130" align="center" />
+          <el-table-column prop="send_time" label="发送时间" min-width="180" align="center" />
+          <el-table-column label="发送者" min-width="150" align="center"><template slot-scope="scope"><span class="sender-cell">批核者:<span class="sender-name">{{ scope.row.sender }}</span></span></template></el-table-column>
+          <el-table-column label="功能" width="150" align="center" fixed="right"><template slot-scope="scope"><el-button type="text" size="small" class="operation-link" @click="viewInfo(scope.row)">查看</el-button><el-button type="text" size="small" class="operation-link danger-link" @click="deleteInfo(scope.row)">删除</el-button></template></el-table-column>
+        </el-table></div>
+        <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="list.page" :page-size="list.limit" :page-sizes="pageSizes" :total="list.total" @size-change="changePageSize('list', $event)" @current-change="changePage('list', $event)" />
+      </template>
+
+      <template v-else>
+        <el-form :model="diary.query" class="operation-search" inline size="small" @submit.native.prevent="searchDiary">
+          <el-form-item label="时间"><el-date-picker v-model="diary.query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+          <el-form-item label="操作员账号"><el-input v-model="diary.query.operator" placeholder="搜索操作员账号" clearable /></el-form-item>
+          <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="searchDiary">查询</el-button><el-button @click="resetDiary">重置</el-button></el-form-item>
+        </el-form>
+
+        <div class="operation-table-wrap"><el-table v-loading="loading" :data="diary.rows" height="100%" class="operation-table">
+          <el-table-column prop="date" label="日期" min-width="180" align="center" />
+          <el-table-column prop="operator" label="操作员账号" min-width="140" align="center" />
+          <el-table-column prop="item" label="操作项目" min-width="140" align="center" />
+          <el-table-column prop="target" label="发送对象" min-width="130" align="center" />
+          <el-table-column prop="old_value" label="旧值" min-width="120" align="center" />
+          <el-table-column prop="new_value" label="新值" min-width="120" align="center" />
+        </el-table></div>
+        <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="diary.page" :page-size="diary.limit" :page-sizes="pageSizes" :total="diary.total" @size-change="changePageSize('diary', $event)" @current-change="changePage('diary', $event)" />
+      </template>
+    </div>
+
+    <Dialog ref="createDialog" :dialog-actions="{ width: '760px', title: '新增发送信息', buttom: false, confirmButtom: false }">
+      <template slot="content">
+        <el-form :model="form" label-width="120px" class="info-dialog-form">
+          <el-form-item label="发件对象"><el-select v-model="form.target" placeholder="请选择"><el-option v-for="item in targetOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
+          <el-form-item label="信息分类"><el-select v-model="form.category" placeholder="请选择"><el-option v-for="item in categoryOptions" :key="item.value" :label="item.label" :value="item.value" /></el-select></el-form-item>
+          <el-form-item label="发件时间"><el-date-picker v-model="form.send_time" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择发送时间" /></el-form-item>
+          <el-form-item label="信息标题"><el-input v-model="form.title" type="textarea" :rows="2" maxlength="500" placeholder="长度限制500个字符" /></el-form-item>
+          <el-form-item label="信息副标题"><el-input v-model="form.subtitle" type="textarea" :rows="2" maxlength="500" placeholder="长度限制500个字符" /></el-form-item>
+          <el-form-item label="信息内容"><el-input v-model="form.content" type="textarea" :rows="4" maxlength="500" placeholder="长度限制500个字符" /></el-form-item>
+          <div class="dialog-actions"><el-button type="primary" @click="submitInfo">发送</el-button></div>
+        </el-form>
+      </template>
+    </Dialog>
+
+    <Dialog ref="viewDialog" :dialog-actions="{ width: '700px', class: 'is-detail-dialog', title: '查看信息', confirmButtom: false, cancelButtom: true, buttonTextLeft: '关闭' }">
+      <template slot="content">
+        <el-form label-width="110px" class="info-detail-form">
+          <el-form-item label="信息类型"><span class="detail-text">{{ detail.info_type }}</span></el-form-item>
+          <el-form-item label="信息标题"><span class="detail-text">{{ detail.title }}</span></el-form-item>
+          <el-form-item label="信息副标题"><span class="detail-text">{{ detail.subtitle || '--' }}</span></el-form-item>
+          <el-form-item label="收件对象"><span class="detail-text">{{ detail.receiver }}</span></el-form-item>
+          <el-form-item label="发送时间"><span class="detail-text">{{ detail.send_time }}</span></el-form-item>
+          <el-form-item label="发送者"><span class="detail-text">{{ detail.sender }}</span></el-form-item>
+          <el-form-item label="信息内容"><div class="detail-content">{{ detail.content }}</div></el-form-item>
+        </el-form>
+      </template>
+    </Dialog>
+  </div>
+</template>
+
+<script>
+import Dialog from '@/components/Dialog/index.vue'
+import { disableFutureDate } from '@/utils/index'
+
+const CATEGORY_OPTIONS = [
+  { label: '优惠活动', value: '优惠活动' },
+  { label: '网站公告', value: '网站公告' },
+  { label: '站内信', value: '站内信' },
+  { label: '活动宣传', value: '活动宣传' }
+]
+
+const TARGET_OPTIONS = ['所有用户', '指定用户', '所有代理', '指定代理']
+
+export default {
+  name: 'InformationManagement',
+  components: { Dialog },
+  data() {
+    return {
+      disableFutureDate,
+      pageSizes: [10, 20, 50, 100],
+      loading: false,
+      activeTab: 'list',
+      categoryOptions: CATEGORY_OPTIONS,
+      targetOptions: TARGET_OPTIONS,
+      listNotes: ['列表里的删除是从用户的信息列表里删除掉这条信息'],
+      list: { page: 1, limit: 20, total: 0, query: { category: '', date_range: [], title: '' }, rows: [] },
+      diary: { page: 1, limit: 20, total: 0, query: { date_range: [], operator: '' }, rows: [] },
+      form: {},
+      detail: {}
+    }
+  },
+  created() {
+    this.loadList()
+    this.loadDiary()
+  },
+  methods: {
+    newForm() {
+      return { target: '所有用户', category: '站内信', send_time: '', title: '', subtitle: '', content: '' }
+    },
+    mockInfoList() {
+      const base = [
+        { info_type: '活动宣传', title: '邀请卖家入驻', receiver: '所有会员' },
+        { info_type: '优惠活动', title: '首充返利活动上线', receiver: '所有会员' },
+        { info_type: '网站公告', title: '系统升级维护通知', receiver: '所有代理' },
+        { info_type: '站内信', title: '账户安全提醒', receiver: '指定会员' }
+      ]
+      return Array.from({ length: 23 }, (_, index) => {
+        const item = base[index % base.length]
+        const day = String((index % 9) + 1).padStart(2, '0')
+        const hour = String((index % 12) + 9).padStart(2, '0')
+        return {
+          id: index + 1,
+          info_type: item.info_type,
+          title: item.title,
+          receiver: item.receiver,
+          send_time: `2025-03-${day} ${hour}:12:09`,
+          sender: 'admin',
+          subtitle: `${item.title}(副标题)`,
+          content: `这是「${item.title}」的信息内容示例,用于验证页面排版与查看弹窗的展示效果。`
+        }
+      })
+    },
+    mockDiaryRows() {
+      const source = [
+        { item: '信息推送', target: '所有人', old_value: '待发送', new_value: '已发送' },
+        { item: '邀请入驻', target: '所有人', old_value: '启用', new_value: '停用' },
+        { item: '优惠活动', target: '指定代理', old_value: '草稿', new_value: '已发布' }
+      ]
+      return Array.from({ length: 17 }, (_, index) => {
+        const row = source[index % source.length]
+        const day = String((index % 9) + 1).padStart(2, '0')
+        return {
+          id: index + 1,
+          date: `2025-03-${day} 22:${String(index % 60).padStart(2, '0')}:43`,
+          operator: 'hope123',
+          item: row.item,
+          target: row.target,
+          old_value: row.old_value,
+          new_value: row.new_value
+        }
+      })
+    },
+    loadList() {
+      this.loading = true
+      const query = this.list.query
+      const [start_date, end_date] = query.date_range || []
+      const rows = this.mockInfoList().filter(row => {
+        const day = row.send_time.slice(0, 10)
+        if (query.category && row.info_type !== query.category) return false
+        if (query.title && !row.title.includes(query.title)) return false
+        if (start_date && day < start_date) return false
+        if (end_date && day > end_date) return false
+        return true
+      })
+      const start = (this.list.page - 1) * this.list.limit
+      this.list.total = rows.length
+      this.list.rows = rows.slice(start, start + this.list.limit)
+      this.loading = false
+    },
+    loadDiary() {
+      const [start_date, end_date] = this.diary.query.date_range || []
+      const rows = this.mockDiaryRows().filter(row => {
+        const day = row.date.slice(0, 10)
+        if (start_date && day < start_date) return false
+        if (end_date && day > end_date) return false
+        return !this.diary.query.operator || row.operator.includes(this.diary.query.operator)
+      })
+      const start = (this.diary.page - 1) * this.diary.limit
+      this.diary.total = rows.length
+      this.diary.rows = rows.slice(start, start + this.diary.limit)
+    },
+    searchList() { this.list.page = 1; this.loadList() },
+    resetList() { this.list.query = { category: '', date_range: [], title: '' }; this.list.page = 1; this.loadList() },
+    searchDiary() { this.diary.page = 1; this.loadDiary() },
+    resetDiary() { this.diary.query = { date_range: [], operator: '' }; this.diary.page = 1; this.loadDiary() },
+    changePage(key, page) { this[key].page = page; key === 'list' ? this.loadList() : this.loadDiary() },
+    changePageSize(key, limit) { this[key].limit = limit; this[key].page = 1; key === 'list' ? this.loadList() : this.loadDiary() },
+    createInfo() { this.form = this.newForm(); this.$refs.createDialog.open() },
+    viewInfo(row) { this.detail = row; this.$refs.viewDialog.open() },
+    deleteInfo(row) {
+      this.$confirm('确定删除这条信息吗?删除的是客户已收到的信息,删除后客户将不再看到该条信息。', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
+        .then(() => {
+          this.list.rows = this.list.rows.filter(item => item.id !== row.id)
+          this.list.total -= 1
+          this.$message.success('删除成功')
+        })
+        .catch(() => {})
+    },
+    submitInfo() {
+      if (!this.form.title) { this.$message.warning('请输入信息标题'); return }
+      this.$refs.createDialog.close()
+      this.$message.success('发送成功')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.operation-setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.operation-setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; padding: 0 20px 12px; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.operation-tabs { flex-shrink: 0; ::v-deep .el-tabs__header { margin: 0; } ::v-deep .el-tabs__item { height: 52px; line-height: 52px; font-weight: 500; } }
+.operation-search { display: flex; flex-wrap: wrap; align-items: center; flex-shrink: 0; padding-top: 16px; ::v-deep .el-form-item { display: flex; flex: 0 0 auto; align-items: center; margin-right: 28px !important; margin-bottom: 14px; white-space: nowrap; } ::v-deep .el-form-item__label { float: none; width: auto !important; padding: 0 8px 0 0; color: #303133; font-weight: 500; line-height: 32px; } ::v-deep .el-form-item__content { display: flex; align-items: center; margin-left: 0 !important; line-height: 32px; } ::v-deep .el-input { width: 180px; } ::v-deep .el-select { width: 180px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-date-editor--daterange.el-input__inner { width: 320px; } .search-actions { margin-right: 0 !important; } }
+.operation-toolbar { display: flex; flex-shrink: 0; align-items: center; min-height: 42px; padding-bottom: 10px; }
+.operation-toolbar-title { color: #303133; font-size: 15px; font-weight: 500; }
+.operation-table-wrap { display: flex; flex: 1; min-height: 0; flex-direction: column; }
+.operation-table { flex: 1; ::v-deep th { height: 44px; background: #f5f7fa; } ::v-deep th .cell { color: #606266 !important; font-weight: 500; } ::v-deep td { height: 49px; } ::v-deep td .cell { color: #606266; font-size: 14px; } ::v-deep .el-table__fixed-right { height: 100% !important; } }
+.operation-link { margin: 0 4px; color: #3e7bfa; } .danger-link { color: #f56c6c; }
+.operation-pagination { flex-shrink: 0; padding-top: 12px; text-align: right; }
+.sender-cell { white-space: nowrap; } .sender-name { color: #3e7bfa; }
+.page-notes { flex-shrink: 0; padding: 16px 0 2px; p { margin: 0 0 2px; color: #f56c6c; font-size: 13px; line-height: 22px; } }
+.info-dialog-form { max-height: 560px; overflow-y: auto; padding: 12px 20px 0; ::v-deep .el-form-item { margin-bottom: 16px; } ::v-deep .el-input, ::v-deep .el-select { width: 280px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-textarea { width: 440px; } ::v-deep .el-textarea__inner { font-family: inherit; } }
+.info-detail-form { padding: 4px 12px 0; ::v-deep .el-form-item { margin-bottom: 12px; } ::v-deep .el-form-item__label { line-height: 22px; } ::v-deep .el-form-item__content { color: #606266; line-height: 22px; } }
+.detail-text { color: #606266; line-height: 22px; word-break: break-all; }
+.detail-content { width: 100%; padding: 10px 12px; background: #f5f7fa; border-radius: 4px; line-height: 22px; white-space: pre-wrap; }
+.dialog-actions { padding: 8px 0 4px; text-align: center; }
+</style>

+ 83 - 0
src/views/PlatformSetting/MaintenanceSetting/index.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="new-app-container layout-vertical setting-page">
+    <div class="setting-panel">
+      <div class="setting-header">
+        <span class="setting-title">维护设定</span>
+        <el-button type="primary" size="small" :loading="loading" @click="submit">提交</el-button>
+      </div>
+
+      <div class="setting-scroll">
+        <div class="setting-block">
+          <el-form :model="form" label-width="130px" class="setting-form">
+            <div class="setting-row">
+              <el-form-item label="维护开启"><el-switch v-model="form.enabled" :active-value="1" :inactive-value="0" /><el-date-picker v-model="form.start_time" class="row-date" type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="请选择维护时间" /></el-form-item>
+              <el-form-item label="维护时区"><el-select v-model="form.timezone" placeholder="请选择"><el-option v-for="item in timezoneOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
+            </div>
+            <div class="setting-row setting-row-top">
+              <el-form-item label="维护装置"><el-checkbox-group v-model="form.devices"><el-checkbox label="H5">H5</el-checkbox><el-checkbox label="App">App</el-checkbox></el-checkbox-group></el-form-item>
+              <el-form-item label="H5/App编辑"><el-input v-model="form.content" type="textarea" :rows="5" placeholder="输入内容" /></el-form-item>
+            </div>
+          </el-form>
+        </div>
+
+        <div class="setting-grid">
+          <div v-for="panel in panels" :key="panel.key" class="setting-block">
+            <div class="setting-block-header">
+              <span class="setting-block-title">{{ panel.title }}</span>
+              <el-switch v-model="panel.enabled" :active-value="1" :inactive-value="0" />
+            </div>
+            <el-form :model="panel" label-width="110px" class="setting-form">
+              <div class="setting-row setting-row-top">
+                <el-form-item label="维护装置"><el-checkbox-group v-model="panel.devices"><el-checkbox label="H5">H5</el-checkbox><el-checkbox label="App">App</el-checkbox></el-checkbox-group></el-form-item>
+                <el-form-item label="内容"><el-input v-model="panel.h5_content" type="textarea" :rows="2" placeholder="输入内容" /><el-input v-model="panel.app_content" class="stack-input" type="textarea" :rows="2" placeholder="输入内容" /></el-form-item>
+              </div>
+            </el-form>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MaintenanceSetting',
+  data() {
+    return {
+      loading: false,
+      timezoneOptions: ['GMT+8', 'GMT+7', 'GMT+9', 'GMT+0'],
+      form: { enabled: 1, start_time: '2025-02-17 16:30:00', timezone: 'GMT+8', devices: ['H5'], content: '' },
+      panels: [
+        { key: 'mall', title: '商城设定', enabled: 1, devices: ['H5'], h5_content: '', app_content: '' },
+        { key: 'subscribe', title: '订阅设定', enabled: 1, devices: ['H5'], h5_content: '', app_content: '' },
+        { key: 'syoai', title: 'SYOAI设定', enabled: 1, devices: ['H5'], h5_content: '', app_content: '' },
+        { key: 'ktv', title: 'KTV设定', enabled: 1, devices: ['H5'], h5_content: '', app_content: '' }
+      ]
+    }
+  },
+  methods: {
+    submit() {
+      this.loading = true
+      setTimeout(() => {
+        this.loading = false
+        this.$message.success('提交成功')
+      }, 300)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.setting-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; padding: 16px 20px 12px; }
+.setting-title { color: #303133; font-size: 15px; font-weight: 500; }
+.setting-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 20px 16px; }
+.setting-block { margin-bottom: 16px; padding: 12px 16px 4px; background: #f0f1f3; border-radius: 4px; }
+.setting-block-header { display: flex; align-items: center; justify-content: space-between; padding-bottom: 6px; }
+.setting-block-title { color: #303133; font-size: 15px; font-weight: 500; }
+.setting-row { display: flex; align-items: flex-start; }
+.setting-row-top ::v-deep .el-form-item__content { align-items: flex-start; }
+.setting-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
+.setting-form { ::v-deep .el-form-item { flex: 1; margin-bottom: 12px; } ::v-deep .el-input, ::v-deep .el-select { width: 200px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-textarea__inner { width: 260px; font-family: inherit; } .row-date { margin-left: 12px; width: 220px !important; } .stack-input { margin-top: 8px; } }
+</style>

+ 99 - 0
src/views/PlatformSetting/MallSetting/index.vue

@@ -0,0 +1,99 @@
+<template>
+  <div class="new-app-container layout-vertical setting-page">
+    <div class="setting-panel">
+      <div class="setting-header">
+        <span class="setting-title">商城设定</span>
+      </div>
+
+      <div class="setting-scroll">
+        <div class="setting-block">
+          <el-form :model="form" label-width="150px" class="setting-form">
+            <div class="setting-row">
+              <el-form-item label="网站标题"><el-input v-model="form.site_title" placeholder="请输入网站标题" /></el-form-item>
+              <el-form-item label="网站品牌"><el-input v-model="form.site_brand" placeholder="请输入网站品牌" /></el-form-item>
+              <el-form-item label="网站域名"><el-input v-model="form.site_domain" placeholder="请输入网站域名" /></el-form-item>
+            </div>
+            <div class="setting-row">
+              <el-form-item label="钱包最低入款额度"><el-input v-model="form.wallet_min_deposit" placeholder="请输入金额" /></el-form-item>
+              <el-form-item label="网关最低入款额度"><el-input v-model="form.gateway_min_deposit" placeholder="请输入金额" /></el-form-item>
+            </div>
+            <div class="setting-row">
+              <el-form-item label="钱包最低提款额度"><el-input v-model="form.wallet_min_withdraw" placeholder="请输入金额" /></el-form-item>
+              <el-form-item label="提现手续设定"><template slot="label"><span class="red-label">提现手续设定</span></template><el-input v-model="form.withdraw_fee" placeholder="请输入"><template slot="append">%</template></el-input></el-form-item>
+            </div>
+            <div class="setting-row">
+              <el-form-item label="前台提款显示高峰期"><el-switch v-model="form.withdraw_peak" :active-value="1" :inactive-value="0" /></el-form-item>
+              <el-form-item label="当前提款未审核笔数"><el-input v-model="form.pending_withdraw_count" placeholder="请输入笔数" /></el-form-item>
+            </div>
+          </el-form>
+        </div>
+
+        <div class="setting-block">
+          <div class="setting-block-header"><span class="setting-block-title">收款方式设定</span></div>
+          <el-form :model="form" label-width="200px" class="setting-form">
+            <div class="setting-row">
+              <el-form-item label="支付宝账号"><el-switch v-model="form.alipay_enabled" :active-value="1" :inactive-value="0" /></el-form-item>
+              <el-form-item label="微信收款码"><el-switch v-model="form.wechat_enabled" :active-value="1" :inactive-value="0" /></el-form-item>
+            </div>
+            <div class="setting-row">
+              <el-form-item label="银行卡收款信息仅支持一张"><el-switch v-model="form.single_bank_card" :active-value="1" :inactive-value="0" /></el-form-item>
+            </div>
+          </el-form>
+        </div>
+
+        <div class="setting-footer">
+          <el-button type="primary" :loading="loading" @click="submit">提交</el-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'MallSetting',
+  data() {
+    return {
+      loading: false,
+      form: {
+        site_title: '原味秀',
+        site_brand: '原味秀',
+        site_domain: 'showyw.com',
+        wallet_min_deposit: '20',
+        gateway_min_deposit: '50',
+        wallet_min_withdraw: '100',
+        withdraw_fee: '15',
+        withdraw_peak: 1,
+        pending_withdraw_count: '100',
+        alipay_enabled: 1,
+        wechat_enabled: 1,
+        single_bank_card: 1
+      }
+    }
+  },
+  methods: {
+    submit() {
+      this.loading = true
+      setTimeout(() => {
+        this.loading = false
+        this.$message.success('提交成功')
+      }, 300)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.setting-header { display: flex; flex-shrink: 0; align-items: center; justify-content: space-between; padding: 16px 20px 12px; }
+.setting-title { color: #303133; font-size: 15px; font-weight: 500; }
+.setting-scroll { flex: 1; min-height: 0; overflow-y: auto; padding: 0 20px 16px; }
+.setting-block { margin-bottom: 16px; padding: 12px 16px 4px; background: #f0f1f3; border-radius: 4px; }
+.setting-block-header { padding-bottom: 8px; }
+.setting-block-title { color: #303133; font-size: 15px; font-weight: 500; }
+.setting-row { display: flex; flex-wrap: wrap; }
+.setting-form { ::v-deep .el-form-item { flex: 0 0 auto; margin-right: 24px; margin-bottom: 16px; } ::v-deep .el-input { width: 180px; } ::v-deep .el-input-group { width: 220px; } }
+.red-label { color: #3e7bfa; }
+.setting-footer { padding-top: 4px; text-align: left; ::v-deep .el-button { padding: 12px 40px; font-size: 15px; } }
+</style>

+ 117 - 0
src/views/ReportQuery/CapitalFlow/index.vue

@@ -0,0 +1,117 @@
+<template>
+  <div class="new-app-container layout-vertical operation-setting-page">
+    <div class="operation-setting-panel">
+      <el-form :model="query" class="operation-search" inline size="small" @submit.native.prevent="search">
+        <el-form-item label="时间"><el-date-picker v-model="query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+        <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="search">查询</el-button><el-button @click="reset">重置</el-button></el-form-item>
+      </el-form>
+
+      <div class="flow-block">
+        <div class="flow-block-title">总计统计(总资金流 = 总盈利 - 总存款 - 存款手续费 - 总提款 - 提款手续费 - 总优惠)</div>
+        <el-table :data="totalRows" class="flow-total-table">
+          <el-table-column prop="profit" label="总盈利" min-width="110" align="center" />
+          <el-table-column prop="deposit" label="总存款" min-width="110" align="center" />
+          <el-table-column prop="deposit_fee" label="存款手续费" min-width="110" align="center" />
+          <el-table-column prop="withdraw" label="总提款" min-width="110" align="center" />
+          <el-table-column prop="withdraw_fee" label="提款手续费" min-width="110" align="center" />
+          <el-table-column prop="coupon" label="总优惠" min-width="110" align="center" />
+          <el-table-column label="总资金流" min-width="110" align="center"><template slot-scope="scope"><span :class="{ 'value-negative': isNegative(scope.row.capital_flow) }">{{ scope.row.capital_flow }}</span></template></el-table-column>
+        </el-table>
+      </div>
+
+      <div class="flow-block flow-daily">
+        <div class="flow-block-title">每日统计</div>
+        <div class="flow-table-wrap">
+          <el-table v-loading="loading" :data="daily.rows" height="100%" class="flow-table">
+            <el-table-column prop="date" label="日期" min-width="130" align="center" />
+            <el-table-column prop="profit" label="总盈利" min-width="110" align="center" />
+            <el-table-column prop="deposit" label="总存款" min-width="110" align="center" />
+            <el-table-column prop="deposit_fee" label="存款手续费" min-width="110" align="center" />
+            <el-table-column prop="withdraw" label="总提款" min-width="110" align="center" />
+            <el-table-column prop="withdraw_fee" label="提款手续费" min-width="110" align="center" />
+            <el-table-column prop="coupon" label="总优惠" min-width="110" align="center" />
+            <el-table-column label="总资金流" min-width="110" align="center"><template slot-scope="scope"><span :class="{ 'value-negative': isNegative(scope.row.capital_flow) }">{{ scope.row.capital_flow }}</span></template></el-table-column>
+          </el-table>
+        </div>
+        <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="daily.page" :page-size="daily.limit" :page-sizes="pageSizes" :total="daily.total" @size-change="changePageSize" @current-change="changePage" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { disableFutureDate } from '@/utils/index'
+
+export default {
+  name: 'CapitalFlow',
+  data() {
+    return {
+      disableFutureDate,
+      pageSizes: [10, 20, 50, 100],
+      loading: false,
+      query: { date_range: [] },
+      totalRows: [
+        { profit: '74,919', deposit: '120,100', deposit_fee: '6,206.3', withdraw: '45,973', withdraw_fee: '1,379.19', coupon: '1,834.43', capital_flow: '-100,573.92' }
+      ],
+      daily: { page: 1, limit: 15, total: 0, rows: [] }
+    }
+  },
+  created() {
+    this.loadDaily()
+  },
+  methods: {
+    isNegative(value) {
+      return String(value).trim().startsWith('-')
+    },
+    mockDailyRows() {
+      return Array.from({ length: 24 }, (_, index) => {
+        const day = String(24 - index).padStart(2, '0')
+        const profit = 18423.17 + index * 137.4
+        const deposit = 17800 + index * 210.5
+        const deposit_fee = 890.4 + index * 12.3
+        const withdraw = 2659 + index * 88.6
+        const withdraw_fee = 79.7 + index * 3.1
+        const coupon = 289.32 + index * 7.5
+        const capital_flow = profit - deposit - deposit_fee - withdraw - withdraw_fee - coupon
+        return {
+          id: index + 1,
+          date: `2025-03-${day}`,
+          profit: profit.toFixed(2),
+          deposit: deposit.toFixed(2),
+          deposit_fee: deposit_fee.toFixed(2),
+          withdraw: withdraw.toFixed(2),
+          withdraw_fee: withdraw_fee.toFixed(2),
+          coupon: coupon.toFixed(2),
+          capital_flow: capital_flow.toFixed(2)
+        }
+      })
+    },
+    loadDaily() {
+      this.loading = true
+      const rows = this.mockDailyRows()
+      const start = (this.daily.page - 1) * this.daily.limit
+      this.daily.total = rows.length
+      this.daily.rows = rows.slice(start, start + this.daily.limit)
+      this.loading = false
+    },
+    search() { this.daily.page = 1; this.loadDaily() },
+    reset() { this.query.date_range = []; this.daily.page = 1; this.loadDaily() },
+    changePage(page) { this.daily.page = page; this.loadDaily() },
+    changePageSize(limit) { this.daily.limit = limit; this.daily.page = 1; this.loadDaily() }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.operation-setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.operation-setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; padding: 0 20px 12px; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.operation-search { display: flex; flex-wrap: wrap; align-items: center; flex-shrink: 0; padding-top: 16px; ::v-deep .el-form-item { display: flex; flex: 0 0 auto; align-items: center; margin-right: 28px !important; margin-bottom: 14px; white-space: nowrap; } ::v-deep .el-form-item__label { float: none; width: auto !important; padding: 0 8px 0 0; color: #303133; font-weight: 500; line-height: 32px; } ::v-deep .el-form-item__content { display: flex; align-items: center; margin-left: 0 !important; line-height: 32px; } ::v-deep .el-date-editor--daterange.el-input__inner { width: 320px; } .search-actions { margin-right: 0 !important; } }
+.flow-block { flex-shrink: 0; margin-bottom: 16px; padding: 8px 12px; border: 1px solid #ebeef5; border-radius: 4px; }
+.flow-block-title { padding-bottom: 4px; color: #303133; font-size: 15px; font-weight: 500; }
+.flow-total-table { ::v-deep th { height: 42px; background: #f5f7fa; } ::v-deep th .cell { color: #606266 !important; font-weight: 500; } ::v-deep td { height: 48px; } ::v-deep td .cell { color: #606266; font-size: 14px; } }
+.flow-daily { display: flex; flex: 1; min-height: 0; flex-direction: column; }
+.flow-table-wrap { display: flex; flex: 1; min-height: 0; flex-direction: column; }
+.flow-table { flex: 1; ::v-deep th { height: 42px; background: #f5f7fa; } ::v-deep th .cell { color: #606266 !important; font-weight: 500; } ::v-deep td { height: 48px; } ::v-deep td .cell { color: #606266; font-size: 14px; } }
+.value-negative { color: #f56c6c; }
+.operation-pagination { flex-shrink: 0; padding-top: 12px; text-align: right; }
+</style>

+ 82 - 0
src/views/ReportQuery/RankingList/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="new-app-container layout-vertical operation-setting-page">
+    <div class="operation-setting-panel">
+      <el-form :model="query" class="operation-search" inline size="small" @submit.native.prevent="search">
+        <el-form-item label="项目类别"><el-select v-model="query.category" placeholder="请选择" clearable><el-option v-for="item in categoryOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
+        <el-form-item label="时间"><el-date-picker v-model="query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+        <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="search">查询</el-button><el-button @click="reset">重置</el-button></el-form-item>
+      </el-form>
+
+      <div class="operation-table-wrap"><el-table v-loading="loading" :data="rows" height="100%" class="operation-table">
+        <el-table-column prop="rank" label="排名" width="90" align="center" />
+        <el-table-column prop="account" label="会账号" min-width="130" align="center" />
+        <el-table-column prop="nickname" label="会员昵称" min-width="140" align="center" />
+        <el-table-column prop="deposit" label="存款总额" min-width="130" align="center" />
+        <el-table-column prop="consume" label="消费总额" min-width="130" align="center" />
+        <el-table-column prop="sale" label="销售总额" min-width="130" align="center" />
+        <el-table-column prop="withdraw" label="提款总额" min-width="130" align="center" />
+        <el-table-column prop="online_duration" label="在线时间(m)" min-width="130" align="center" />
+      </el-table></div>
+      <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="page" :page-size="limit" :page-sizes="pageSizes" :total="total" @size-change="changePageSize" @current-change="changePage" />
+    </div>
+  </div>
+</template>
+
+<script>
+import { disableFutureDate } from '@/utils/index'
+
+export default {
+  name: 'RankingList',
+  data() {
+    return {
+      disableFutureDate,
+      pageSizes: [10, 20, 50, 100],
+      loading: false,
+      categoryOptions: ['存款总额', '消费总额', '销售总额', '提款总额', '在线时间'],
+      query: { category: '', date_range: [] },
+      page: 1,
+      limit: 15,
+      total: 0,
+      rows: []
+    }
+  },
+  created() {
+    this.loadList()
+  },
+  methods: {
+    mockRows() {
+      const source = [
+        { account: 'daw22', nickname: '味道王', deposit: 45973, consume: 13790, sale: null, withdraw: null, online_duration: 890 },
+        { account: 'tsa922', nickname: '原味文章', deposit: 35973, consume: 23790, sale: null, withdraw: null, online_duration: 1890 },
+        { account: 'ir192', nickname: '卖家小屋', deposit: 56, consume: 12, sale: 45677, withdraw: 3004, online_duration: 2890 },
+        { account: 'hop123', nickname: '幸运星', deposit: 28900, consume: 8900, sale: 1200, withdraw: 900, online_duration: 1450 }
+      ]
+      return Array.from({ length: 21 }, (_, index) => {
+        const row = source[index % source.length]
+        return { ...row, id: index + 1, rank: index + 1, account: `${row.account}${index >= source.length ? index : ''}` }
+      })
+    },
+    loadList() {
+      this.loading = true
+      const rows = this.mockRows()
+      const start = (this.page - 1) * this.limit
+      this.total = rows.length
+      this.rows = rows.slice(start, start + this.limit)
+      this.loading = false
+    },
+    search() { this.page = 1; this.loadList() },
+    reset() { this.query = { category: '', date_range: [] }; this.page = 1; this.loadList() },
+    changePage(page) { this.page = page; this.loadList() },
+    changePageSize(limit) { this.limit = limit; this.page = 1; this.loadList() }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.operation-setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.operation-setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; padding: 0 20px 12px; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.operation-search { display: flex; flex-wrap: wrap; align-items: center; flex-shrink: 0; padding-top: 16px; ::v-deep .el-form-item { display: flex; flex: 0 0 auto; align-items: center; margin-right: 28px !important; margin-bottom: 14px; white-space: nowrap; } ::v-deep .el-form-item__label { float: none; width: auto !important; padding: 0 8px 0 0; color: #303133; font-weight: 500; line-height: 32px; } ::v-deep .el-form-item__content { display: flex; align-items: center; margin-left: 0 !important; line-height: 32px; } ::v-deep .el-select { width: 160px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-date-editor--daterange.el-input__inner { width: 320px; } .search-actions { margin-right: 0 !important; } }
+.operation-table-wrap { display: flex; flex: 1; min-height: 0; flex-direction: column; }
+.operation-table { flex: 1; ::v-deep th { height: 44px; background: #f5f7fa; } ::v-deep th .cell { color: #606266 !important; font-weight: 500; } ::v-deep td { height: 49px; } ::v-deep td .cell { color: #606266; font-size: 14px; } }
+.operation-pagination { flex-shrink: 0; padding-top: 12px; text-align: right; }
+</style>

+ 166 - 0
src/views/ReportQuery/ReportSummary/index.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="new-app-container layout-vertical operation-setting-page">
+    <div class="operation-setting-panel">
+      <el-tabs v-model="activeTab" class="operation-tabs">
+        <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name" />
+      </el-tabs>
+
+      <el-form :model="query" class="operation-search" inline size="small" @submit.native.prevent="search">
+        <el-form-item label="装置"><el-select v-model="query.device" placeholder="请选择" clearable><el-option label="全部" value="全部" /><el-option label="H5" value="H5" /><el-option label="App" value="App" /></el-select></el-form-item>
+        <el-form-item label="结算状态"><el-select v-model="query.settle_status" placeholder="请选择" clearable><el-option v-for="item in settleStatusOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
+        <el-form-item label="时间"><el-date-picker v-model="query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+        <el-form-item label="会员账号"><el-input v-model="query.member" placeholder="搜索会员账号" clearable /></el-form-item>
+        <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="search">查询</el-button><el-button @click="reset">重置</el-button></el-form-item>
+      </el-form>
+
+      <div class="report-notes">
+        <p v-for="(note, index) in activeNotes" :key="index">{{ note }}</p>
+      </div>
+
+      <div class="report-blocks">
+        <div v-for="block in blocks" :key="block.key" class="report-block">
+          <div class="report-block-title">
+            <el-button type="text" class="report-block-link" @click="openDetail(block)">{{ block.title }}</el-button>
+          </div>
+          <el-table :data="block.rows" class="report-block-table" @row-click="openDetail(block)">
+            <el-table-column prop="device" label="装置" min-width="90" align="center" />
+            <el-table-column prop="deposit" label="存款总额" min-width="110" align="center" />
+            <el-table-column prop="order" label="订单总额" min-width="110" align="center" />
+            <el-table-column prop="sale" label="出售总额" min-width="110" align="center" />
+            <el-table-column prop="withdraw" label="提现总额" min-width="110" align="center" />
+            <el-table-column label="公司盈利" min-width="110" align="center"><template slot-scope="scope"><span class="value-profit">{{ scope.row.profit }}</span></template></el-table-column>
+            <el-table-column label="在线率" min-width="100" align="center"><template slot-scope="scope"><span class="value-online">{{ scope.row.online_rate }}</span></template></el-table-column>
+            <el-table-column label="有效留存会员" min-width="120" align="center"><template slot-scope="scope"><span class="value-retention">{{ scope.row.retention }}</span></template></el-table-column>
+            <el-table-column prop="coupon" label="优惠金" min-width="100" align="center" />
+          </el-table>
+        </div>
+      </div>
+    </div>
+
+    <Dialog ref="detailDialog" :dialog-actions="{ width: '1100px', class: 'is-detail-dialog', title: detailTitle, buttom: false, confirmButtom: false }">
+      <template slot="content">
+        <el-form :model="detail.query" class="detail-search" inline size="small" @submit.native.prevent="searchDetail">
+          <el-form-item label="结算状态"><el-select v-model="detail.query.settle_status" placeholder="请选择" clearable><el-option v-for="item in settleStatusOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
+          <el-form-item label="时间"><el-date-picker v-model="detail.query.date_range" type="daterange" value-format="yyyy-MM-dd" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="disableFutureDate" /></el-form-item>
+          <el-form-item label="代理账号"><el-input v-model="detail.query.agent" placeholder="搜索代理号" clearable /></el-form-item>
+          <el-form-item class="search-actions"><el-button type="primary" :loading="loading" @click="searchDetail">查询</el-button><el-button @click="resetDetail">重置</el-button></el-form-item>
+        </el-form>
+        <el-table v-loading="loading" :data="detail.rows" height="420" class="detail-table">
+          <el-table-column prop="agent" label="代理账号" min-width="120" align="center" />
+          <el-table-column prop="deposit" label="存款总额" min-width="110" align="center" />
+          <el-table-column prop="order" label="订单总额" min-width="110" align="center" />
+          <el-table-column prop="sale" label="出售总额" min-width="110" align="center" />
+          <el-table-column prop="withdraw" label="提现总额" min-width="110" align="center" />
+          <el-table-column label="公司盈利" min-width="110" align="center"><template slot-scope="scope"><span class="value-profit">{{ scope.row.profit }}</span></template></el-table-column>
+          <el-table-column label="在线率" min-width="100" align="center"><template slot-scope="scope"><span class="value-online">{{ scope.row.online_rate }}</span></template></el-table-column>
+          <el-table-column label="有效留存会员" min-width="120" align="center"><template slot-scope="scope"><span class="value-retention">{{ scope.row.retention }}</span></template></el-table-column>
+          <el-table-column prop="coupon" label="优惠金" min-width="100" align="center" />
+        </el-table>
+        <el-pagination background class="operation-pagination" layout="total, sizes, prev, pager, next, jumper" :current-page="detail.page" :page-size="detail.limit" :page-sizes="pageSizes" :total="detail.total" @size-change="changeDetailSize" @current-change="changeDetailPage" />
+      </template>
+    </Dialog>
+  </div>
+</template>
+
+<script>
+import Dialog from '@/components/Dialog/index.vue'
+import { disableFutureDate } from '@/utils/index'
+
+const SETTLE_STATUS_OPTIONS = ['待结算', '已结算']
+
+export default {
+  name: 'ReportSummary',
+  components: { Dialog },
+  data() {
+    return {
+      disableFutureDate,
+      pageSizes: [10, 20, 50, 100],
+      loading: false,
+      activeTab: 'business',
+      settleStatusOptions: SETTLE_STATUS_OPTIONS,
+      tabs: [
+        { name: 'business', label: '营业报表', notes: ['结束状态:待结算 已结算', '1.有效留存,只要存款超过设置的最低存款,并进行一次消费就算一个有效会员'] },
+        { name: 'promote', label: '推广报表', notes: ['推广报表 主要统计 非平台运营(三方合作)的营业报表', '结束状态:待结算 已结算', '1.有效留存,只要存款超过设置的最低存款,并进行一次消费就算一个有效会员'] },
+        { name: 'share', label: '分享赚钱', notes: ['分享赚钱 主要统计 平台用户分享用户的营业报表', '结束状态:待结算 已结算', '1.有效留存,只要存款超过设置的最低存款,并进行一次消费就算一个有效会员'] }
+      ],
+      blocks: [],
+      query: { device: '', settle_status: '', date_range: [], member: '' },
+      detailTitle: '所有订单',
+      detail: { page: 1, limit: 10, total: 0, query: { settle_status: '', date_range: [], agent: '' }, rows: [] }
+    }
+  },
+  computed: {
+    activeNotes() {
+      const tab = this.tabs.find(item => item.name === this.activeTab)
+      return tab ? tab.notes : []
+    }
+  },
+  created() {
+    this.loadBlocks()
+  },
+  methods: {
+    seed(offset) {
+      return [
+        { key: 'all', title: '所有订单', device: '全部', deposit: 302982 + offset, order: 290319, sale: 202100, withdraw: 159100, profit: 501199, online_rate: '69%', retention: 5024, coupon: 3098 },
+        { key: 'mall', title: '商城订单', device: '全部', deposit: 230400, order: 199100, sale: 18970, withdraw: 159100, profit: 23865, online_rate: '51%', retention: 3028, coupon: 3098 },
+        { key: 'subscribe', title: '订阅订单', device: '全部', deposit: 230400, order: 199100, sale: 18970, withdraw: 159100, profit: 23865, online_rate: '51%', retention: 3028, coupon: 3098 },
+        { key: 'sayai', title: 'SAYAI订单', device: '全部', deposit: 230400, order: 199100, sale: 18970, withdraw: 159100, profit: 23865, online_rate: '51%', retention: 3028, coupon: 3098 }
+      ]
+    },
+    loadBlocks() {
+      const offset = this.activeTab === 'business' ? 0 : this.activeTab === 'promote' ? 1000 : 2000
+      this.blocks = this.seed(offset).map(block => ({ ...block, rows: [{ ...block, key: `${block.key}-total` }] }))
+    },
+    mockDetailRows() {
+      const base = [
+        { agent: 'dailiai123', deposit: 202982, order: 203190, sale: 2100, withdraw: 13000, profit: 41199, online_rate: '69%', retention: 1024, coupon: 2098 },
+        { agent: 'dailiai99', deposit: 9820, order: 2190, sale: 100, withdraw: 900, profit: 599, online_rate: '6%', retention: 124, coupon: 98 },
+        { agent: 'dailiai456', deposit: 132400, order: 99800, sale: 8900, withdraw: 45100, profit: 23865, online_rate: '51%', retention: 3028, coupon: 3098 }
+      ]
+      return Array.from({ length: 18 }, (_, index) => {
+        const row = base[index % base.length]
+        return { ...row, id: index + 1, agent: `${row.agent}${index >= base.length ? index : ''}` }
+      })
+    },
+    loadDetail() {
+      this.loading = true
+      const query = this.detail.query
+      const rows = this.mockDetailRows().filter(row => !query.agent || row.agent.includes(query.agent))
+      const start = (this.detail.page - 1) * this.detail.limit
+      this.detail.total = rows.length
+      this.detail.rows = rows.slice(start, start + this.detail.limit)
+      this.loading = false
+    },
+    search() { this.loadBlocks() },
+    reset() { this.query = { device: '', settle_status: '', date_range: [], member: '' }; this.loadBlocks() },
+    openDetail(block) {
+      this.detailTitle = block.title
+      this.detail.page = 1
+      this.detail.query = { settle_status: '', date_range: [], agent: '' }
+      this.loadDetail()
+      this.$refs.detailDialog.open()
+    },
+    searchDetail() { this.detail.page = 1; this.loadDetail() },
+    resetDetail() { this.detail.query = { settle_status: '', date_range: [], agent: '' }; this.detail.page = 1; this.loadDetail() },
+    changeDetailPage(page) { this.detail.page = page; this.loadDetail() },
+    changeDetailSize(limit) { this.detail.limit = limit; this.detail.page = 1; this.loadDetail() }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.operation-setting-page { height: 100%; padding: 16px; background: #f5f7fa; box-sizing: border-box; }
+.operation-setting-panel { display: flex; flex: 1; flex-direction: column; min-width: 0; min-height: 0; padding: 0 20px 12px; overflow: hidden; background: #fff; box-shadow: 0 1px 8px rgba(0, 0, 0, 0.08); }
+.operation-tabs { flex-shrink: 0; ::v-deep .el-tabs__header { margin: 0; } ::v-deep .el-tabs__item { height: 52px; line-height: 52px; font-weight: 500; } }
+.operation-search { display: flex; flex-wrap: wrap; align-items: center; flex-shrink: 0; padding-top: 16px; ::v-deep .el-form-item { display: flex; flex: 0 0 auto; align-items: center; margin-right: 28px !important; margin-bottom: 14px; white-space: nowrap; } ::v-deep .el-form-item__label { float: none; width: auto !important; padding: 0 8px 0 0; color: #303133; font-weight: 500; line-height: 32px; } ::v-deep .el-form-item__content { display: flex; align-items: center; margin-left: 0 !important; line-height: 32px; } ::v-deep .el-input { width: 180px; } ::v-deep .el-select { width: 150px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-date-editor--daterange.el-input__inner { width: 300px; } .search-actions { margin-right: 0 !important; } }
+.report-notes { flex-shrink: 0; padding-bottom: 6px; p { margin: 0; color: #f56c6c; font-size: 13px; line-height: 22px; } }
+.report-blocks { display: flex; flex: 1; min-height: 0; flex-direction: column; overflow-y: auto; padding-right: 4px; }
+.report-block { flex-shrink: 0; margin-bottom: 16px; padding: 4px 12px 8px; border: 1px solid #ebeef5; border-radius: 4px; }
+.report-block-title { padding: 6px 0 2px; }
+.report-block-link { padding: 0; color: #3e7bfa; font-size: 14px; font-weight: 500; text-decoration: underline; }
+.report-block-table { ::v-deep th { height: 40px; background: #f5f7fa; } ::v-deep th .cell { color: #606266 !important; font-weight: 500; } ::v-deep td { height: 44px; } ::v-deep td .cell { color: #606266; font-size: 14px; } ::v-deep .el-table__row { cursor: pointer; } }
+.value-profit { color: #3e7bfa; } .value-online, .value-retention { color: #13ce66; }
+.detail-search { margin-bottom: 12px; ::v-deep .el-form-item { margin-right: 16px; margin-bottom: 8px; } ::v-deep .el-input { width: 160px; } ::v-deep .el-select { width: 130px; } ::v-deep .el-select .el-input { width: 100%; } ::v-deep .el-date-editor--daterange.el-input__inner { width: 280px; } }
+.detail-table ::v-deep th { height: 42px; background: #f5f7fa; }
+.operation-pagination { flex-shrink: 0; padding-top: 12px; text-align: right; }
+</style>