zhengnaiwen_citu 1 vecka sedan
förälder
incheckning
4a98860a31

+ 11 - 6
src/api/dataChart.js

@@ -6,12 +6,17 @@ export function getAsk (data, config) {
   return http.post('/vanna/v0/ask_agent', data, config)
 }
 
-// 提交到正确训练集
-export function submitTrainingCorrect (data) {
-  return http.post('/vanna/v0/citu_train_question_sql', data)
+// 创建新的反馈记录
+export function addFeedback (data) {
+  return http.post('/vanna/v0/qa_feedback/add', data)
 }
 
-// 提交到错误训练集
-export function submitTrainingError (data) {
-  return http.post('/vanna/v0/training_error_question_sql', data)
+// 获取指定用户user_id的最近N次的会话信息
+export function getConversations (data) {
+  return http.get('/vanna/v0/user/guest/conversations', data)
+}
+
+// 获取指定用户user_id的最近N次的会话信息
+export function getConversationsById (id) {
+  return http.get(`/vanna/v0/conversation/${id}/messages`)
 }

+ 1 - 44
src/layout/index.vue

@@ -1,43 +1,5 @@
 <template>
   <div class="fullScreen d-flex">
-    <v-navigation-drawer
-      class="grey lighten-4 navigation"
-      dark
-      permanent
-    >
-      <template v-slot:prepend>
-        <v-list-item light>
-          <v-list-item-content>
-            <v-list-item-title class="text-h6">AI取数</v-list-item-title>
-            <!-- <v-list-item-subtitle>Logged In</v-list-item-subtitle> -->
-          </v-list-item-content>
-        </v-list-item>
-      </template>
-      <v-divider light></v-divider>
-      <!-- <v-list>
-        <v-list-item
-          v-for="item in items"
-          :key="item.title"
-          link
-        >
-          <v-list-item-icon>
-            <v-icon>{{ item.icon }}</v-icon>
-          </v-list-item-icon>
-
-          <v-list-item-content>
-            <v-list-item-title>{{ item.title }}</v-list-item-title>
-          </v-list-item-content>
-        </v-list-item>
-      </v-list> -->
-
-      <template v-slot:append>
-        <div class="pa-2">
-          <v-btn block @click="handleLogout">
-            Logout
-          </v-btn>
-        </div>
-      </template>
-    </v-navigation-drawer>
     <keep-alive v-if="$route.meta.keepAlive">
       <router-view />
     </keep-alive>
@@ -47,12 +9,7 @@
 
 <script>
 export default {
-  name: 'LayoutIndex',
-  methods: {
-    handleLogout () {
-      this.$store.dispatch('user/userLogout')
-    }
-  }
+  name: 'LayoutIndex'
 }
 </script>
 

+ 0 - 180
src/views/dataChart/dataChartEdit.vue

@@ -1,180 +0,0 @@
-<template>
-  <div class="d-flex chart heightFull widthFull">
-    <div v-if="showChart" class="chart-list heightFull overflow-y-auto mr-3">
-      <div v-for="(chart, key) in Charts" :key="key" class="chart-type mb-3" @click="onChange(key)">
-        <div>
-          <span class="mdi" :class="chart.icon"></span>
-        </div>
-        <div>
-          {{ chart.title }}
-        </div>
-      </div>
-    </div>
-
-    <div class="chart-content d-flex heightFull">
-      <FullscreenToggle v-if="showChart" class="chart-content-show heightFull white mr-3 overflow-hidden pa-3 position-relative" ref="box">
-        <template v-slot="{ toggle, isFullscreen }">
-          <div class="position-absolute d-flex flex-column" style="right: 10px; top: 80px; z-index: 999">
-            <v-btn
-              icon
-              color="primary"
-              @click="toggle"
-            >
-              <v-icon>{{ isFullscreen ? 'mdi-arrow-collapse' : 'mdi-arrow-expand'}}</v-icon>
-            </v-btn>
-            <v-btn
-              icon
-              color="primary"
-              class="mt-3"
-              @click="onClose"
-            >
-              <v-icon>mdi-close</v-icon>
-            </v-btn>
-          </div>
-          <InitChart ref="chart" class="heightFull widthFull"></InitChart>
-        </template>
-      </FullscreenToggle>
-      <DataChartEditChat :class="showChart ? 'widthHalf' : 'widthFull'" @render="onRender"></DataChartEditChat>
-    </div>
-  </div>
-</template>
-
-<script>
-// 属性模块
-import * as Charts from './utils/options.js'
-import DataChartEditChat from './dataChartEditChat.vue'
-import InitChart from '@/charts/initChart'
-import { cloneDeep } from 'lodash'
-
-import FullscreenToggle from '@/components/FullscreenToggle'
-export default {
-  name: 'dataChartEdit',
-  components: {
-    DataChartEditChat,
-    InitChart,
-    FullscreenToggle
-  },
-  data () {
-    return {
-      showChart: false,
-      Charts,
-      chart: null,
-      chartsOpt: {
-        data: [[]],
-        config: {
-          xAxisData: []
-        },
-        key: null
-      }
-    }
-  },
-  methods: {
-    onClose () {
-      this.showChart = false
-      this.chart = null
-    },
-    onChange (key) {
-      this.chartsOpt.key = key
-      this.setData()
-    },
-    onRender ({ type, data }) {
-      this.showChart = true
-      this.$nextTick(() => {
-        if (!this.chart) {
-          this.chart = this.$refs.chart.init()
-        }
-        this.chartsOpt.config.xAxisData = type
-        this.chartsOpt.data = data
-        this.chartsOpt.key = this.chartsOpt.key || 'bar'
-        this.setData()
-      })
-    },
-    setData () {
-      const { data, key, config } = this.chartsOpt
-      this.chart.showLoading()
-      // 根据key值处理data
-      const _option = cloneDeep(Charts[key].option)
-      const series = _option.series
-      const _data = []
-      if (key === 'pie') {
-        (data || [[]]).forEach(e => {
-          _data.push(e.map((_e, i) => {
-            return {
-              value: _e,
-              name: config.xAxisData[i] ?? _e
-            }
-          }))
-        })
-      } else {
-        _data.push(...data)
-      }
-      const _tem = series[0]
-      const { data: dataSource, ...opt } = _tem
-      data.forEach((d, i) => {
-        series[i] = {
-          data: _data[i],
-          ...opt
-        }
-      })
-      if (_option.xAxis?.data) {
-        _option.xAxis.data = config?.xAxisData ?? data[0].map((e, i) => i)
-      }
-      this.chart.setOption(_option, true)
-      this.chart.hideLoading()
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.heightFull {
-  height: 100%;
-}
-.widthFull {
-  width: 100%;
-}
-
-.widthHalf {
-  width: 50%;
-}
-.chart {
-  &-list {
-    // width: 100px;
-    .chart-type {
-      width: 100%;
-      height: 60px;
-      box-sizing: border-box;
-      display: flex;
-      flex-direction: column;
-      justify-content: center;
-      align-items: center;
-      padding: 10px;
-      border: 1px solid #ccc;
-      border-radius: 5px;
-      cursor: pointer;
-      .mdi {
-        font-size: 24px;
-      }
-      &:hover {
-        background-color: #f5f5f5;
-      }
-    }
-  }
-  &-content {
-    width: 0;
-    flex: 1;
-    &-show {
-      width: 50%;
-      border: 1px solid #ccc !important;
-    }
-  }
-}
-.position {
-  &-relative {
-    position: relative;
-  }
-  &-absolute {
-    position: absolute;
-  }
-}
-</style>

+ 0 - 434
src/views/dataChart/dataChartEditChat.vue

@@ -1,434 +0,0 @@
-<template>
-  <div class="chart-content-chat heightFull d-flex flex-column">
-    <div class="chart-content-chat-title mb-3">
-      <v-tabs>
-        <v-tab>AI 取数</v-tab>
-      </v-tabs>
-    </div>
-    <div class="chart-content-chat-box overflow-y-auto position-relative element" ref="chatBox" v-loading="loading">
-      <div class="pa-3">
-        <div
-          v-for="(item, index) in items"
-          :key="index"
-          :class="['d-flex', 'mb-3', item.type === 1 ? 'flex-row' : 'flex-row-reverse' ]"
-        >
-          <v-avatar color="indigo" size="36">
-            <span class="white--text">{{ item.type === 1 ? 'AI' : 'T' }}</span>
-          </v-avatar>
-          <div :class="[item.type === 1 ? 'ml-3 flex-grow-1 flex-shrink-1' : 'mr-3 box-length-70']">
-            <div
-              :class="['d-flex align-center', `justify-${item.type === 1 ? 'start' : 'end'}`]"
-            >
-              {{ item.type === 1 ? 'AI助手' : '游客' }}
-              <template v-if="item.type === 1">
-                <v-btn
-                  v-if="item.content?.sql"
-                  class="ml-3"
-                  small
-                  elevation="0"
-                  depressed
-                  @click="item.showSnackbar = !item.showSnackbar"
-                >
-                  {{ !item.showSnackbar ? '查看SQL' : '收起SQL'}}
-                </v-btn>
-              </template>
-            </div>
-            <div class="mt-2" :class="{ 'indigo lighten-5 pa-3 rounded': item.type === 2 }">
-              <template v-if="typeof item.content === 'string'">
-                <div>
-                  <span v-if="item.welcome" class="mdi mdi-hand-wave"></span>
-                  {{item.content}}
-                </div>
-                <div v-if="item.welcome">
-                  <div class="mt-1" v-for="_item in item.items" :key="_item">
-                    <span class="defaultLink" @click="onSend(_item)">{{_item}}</span>
-                  </div>
-                </div>
-              </template>
-              <template v-else-if="Object.keys(item.content).length === 0">
-                <span>
-                  正在思考中
-                  <v-progress-circular
-                    indeterminate
-                    size="14"
-                    class="ml-1"
-                    width="2"
-                    color="primary"
-                  ></v-progress-circular>
-                </span>
-              </template>
-              <template v-else>
-                <div>
-                  {{ item.content.summary }}
-                </div>
-                <div v-if="item.showSnackbar" class="pa-3 blue-grey lighten-3 mt-3">
-                  {{ item.content.sql }}
-                </div>
-                <div class="mt-3" v-if="item.content.columns.length">
-                  <div>
-                    <v-menu
-                      v-model="item.showMenu"
-                      :close-on-content-click="false"
-                      :close-on-click="false"
-                      max-width="300"
-                      attach=".chart-content-chat-box"
-                    >
-                      <template v-slot:activator>
-                        <v-btn
-                          @click="item.showMenu = true"
-                          text
-                          color="primary"
-                        >我要画图</v-btn>
-                      </template>
-                      <div class="white">
-                        <v-banner>画图配置</v-banner>
-                        <div class="pa-3">
-                          <v-autocomplete
-                            v-model="item.model.typeAxis"
-                            :items="item.content.columns"
-                            class="mb-3"
-                            outlined
-                            dense
-                            hide-details
-                            label="类型轴"
-                          ></v-autocomplete>
-
-                          <v-autocomplete
-                            v-model="item.model.dataAxis"
-                            :items="item.content.columns"
-                            class="mb-3"
-                            outlined
-                            dense
-                            hide-details
-                            label="数据轴"
-                            multiple
-                            chips
-                            small-chips
-                          ></v-autocomplete>
-                          <div class="text-right">
-                            <v-btn small class="mr-3" @click="item.showMenu = false">关闭</v-btn>
-                            <v-btn small color="primary" @click="onRender(item)">图表预览</v-btn>
-                          </div>
-                        </div>
-                      </div>
-                    </v-menu>
-                  </div>
-                  <v-card flat outlined height="324">
-                    <div class="pa-3">
-                      <v-simple-table
-                        fixed-header
-                        dense
-                        height="300px"
-                      >
-                        <template v-slot:default>
-                          <thead>
-                            <tr>
-                              <th
-                                v-for="header in item.content.columns"
-                                :key="header"
-                                class="text-left"
-                              >{{ header }}</th>
-                            </tr>
-                          </thead>
-                          <tbody>
-                            <tr
-                              v-for="(row, index) in item.content.rows"
-                              :key="index"
-                            >
-                              <td
-                                v-for="header in item.content.columns"
-                                :key="header"
-                                class="text-left"
-                              >{{ row[header] }}</td>
-                            </tr>
-                          </tbody>
-                        </template>
-                      </v-simple-table>
-                    </div>
-                  </v-card>
-                </div>
-                <div class="d-flex align-center" v-if="!item.dataValidation">
-                  您认为结果是否正确
-                  <v-btn
-                    class="ma-2"
-                    text
-                    icon
-                    small
-                    color="blue lighten-2"
-                    @click="onAddTrain(item, true)"
-                  >
-                    <v-icon>mdi-thumb-up</v-icon>
-                  </v-btn>
-
-                  <v-btn
-                    class="ma-2"
-                    text
-                    icon
-                    small
-                    color="red lighten-2"
-                    @click="onAddTrain(item, false)"
-                  >
-                    <v-icon>mdi-thumb-down</v-icon>
-                  </v-btn>
-                </div>
-              </template>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="pa-3 chart-content-chat-btn">
-      <div class="send d-flex align-center justify-center">
-        <div class="send-box">
-          <v-textarea
-            v-model="question"
-            class="send-box-area"
-            auto-grow
-            placeholder="请输入您想问的内容,按 Ctrl+Enter 换行"
-            outlined
-            hide-details
-            no-resize
-            rows="1"
-            @keydown.enter="handleKeyCode($event)"
-          >
-          </v-textarea>
-          <v-btn icon color="primary" class="btn" :disabled="!question || disabled" @click="handleSendMsg">
-            <v-icon>mdi-send</v-icon>
-          </v-btn>
-        </div>
-      </div>
-    </div>
-    <!-- <v-dialog
-      v-model="show"
-      persistent
-      max-width="290"
-    >
-      <v-card>
-        <v-card-title class="text-h5">
-          提示
-        </v-card-title>
-        <v-card-text>是否添加到训练集</v-card-text>
-        <v-card-actions>
-          <v-spacer></v-spacer>
-          <v-btn
-            text
-            @click="show = false"
-          >
-            取消
-          </v-btn>
-          <v-btn
-            color="error darken-1"
-            text
-            @click="onSure(false)"
-          >
-            不添加
-          </v-btn>
-          <v-btn
-            color="green darken-1"
-            text
-            @click="onSure(true)"
-          >
-            添加
-          </v-btn>
-        </v-card-actions>
-      </v-card>
-    </v-dialog> -->
-  </div>
-</template>
-
-<script>
-import {
-  getAsk,
-  submitTrainingCorrect,
-  submitTrainingError
-} from '@/api/dataChart'
-export default {
-  name: 'dataChartEditChat',
-  data () {
-    return {
-      loading: false,
-      disabled: false,
-      question: '',
-      items: [
-        {
-          type: 1,
-          welcome: true,
-          content: '你好,我是您的数据查询小助手,支持查询“高速公路服务区”的相关信息。您可以这样提问: ',
-          items: [
-            '现在一共有多少个服务区?分别归属于哪些管理公司?',
-            '哪个服务区的档口数量最多?',
-            '赣州分公司下,餐饮类档口的日均订单量是多少?'
-          ]
-        }
-      ]
-      // trueData: false
-    }
-  },
-  methods: {
-    handleKeyCode (event) {
-      if (event.keyCode === 13) {
-        if (!event.ctrlKey) {
-          event.preventDefault()
-          this.handleSendMsg()
-        } else {
-          this.question += '\n'
-        }
-      }
-    },
-    onSend (str) {
-      if (this.disabled) {
-        return
-      }
-      this.question = str
-      this.handleSendMsg()
-    },
-    async handleSendMsg () {
-      if (!this.question || this.disabled) {
-        return
-      }
-      this.disabled = true
-
-      const question = this.question
-      this.items.push({
-        type: 2,
-        user: '游客',
-        content: question
-      })
-      this.scrollToBottom()
-      const ask = {
-        type: 1,
-        content: {},
-        showSnackbar: false,
-        dataValidation: false,
-        question, // 记录当前问题
-        showMenu: false,
-        model: {
-          dataAxis: null,
-          typeAxis: null
-        }
-      }
-      this.items.push(ask)
-      this.question = ''
-      try {
-        const { data } = await getAsk({ question })
-        ask.content = data
-        this.scrollToBottom()
-      } catch (error) {
-        ask.content = error.message
-      } finally {
-        this.disabled = false
-      }
-    },
-    scrollToBottom () {
-      this.$nextTick(() => {
-        const box = this.$refs.chatBox
-        if (!box) {
-          return
-        }
-        box.scrollTop = box.scrollHeight
-      })
-    },
-    async onAddTrain (item, bool) {
-      this.loading = true
-      const subApi = bool ? submitTrainingCorrect : submitTrainingError
-      try {
-        await subApi({
-          question: item.question,
-          sql: item.content.sql
-        })
-        item.dataValidation = true
-        // this.$snackbar.success('操作成功')
-      } catch (error) {
-        this.$snackbar.error(error)
-      } finally {
-        this.loading = false
-      }
-    },
-    onRender ({ model, content }) {
-      if (!model.dataAxis || !model.typeAxis) {
-        this.$snackbar.error('请选择数据轴和类型轴')
-        return
-      }
-      const { typeAxis, dataAxis } = model
-      const data = {
-        type: typeAxis ? content.rows.map(e => e[typeAxis]) : [],
-        data: dataAxis ? dataAxis.map(e => content.rows.map(r => r[e])) : []
-      }
-      this.$emit('render', data)
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.heightFull {
-  height: 100%;
-}
-.widthFull {
-  width: 100%;
-}
-.chart-content {
-  &-chat {
-    border: 1px solid #ccc;
-    &-box {
-      height: 0;
-      flex: 1;
-      max-width: 800px;
-      width: 100%;
-      margin: 0 auto;
-    }
-    &-btn {
-      .send {
-        margin: 0 auto;
-        max-width: 800px;
-      }
-    }
-  }
-
-}
-.box-length-70 {
-  max-width: 70%;
-}
-.send {
-  // height: 130px;
-  // margin: 20px 0;
-  padding: 20px;
-  &-box {
-    width: 100%;
-    // max-width: 800px;
-    position: relative;
-    .btn {
-      position: absolute;
-      right: 20px;
-      bottom: 12px;
-    }
-    &-area {
-      position: relative;
-      bottom: 0;
-      ::v-deep textarea {
-        padding: 15px 70px 15px 0 !important;
-        max-height: 300px;
-        min-height: 60px;
-        overflow: auto;
-        margin: 0 !important;
-      }
-    }
-  }
-}
-
-.position {
-  &-relative {
-    position: relative;
-  }
-}
-
-.element {
-  overflow: auto;
-  scrollbar-width: none; /* Firefox */
-  -ms-overflow-style: none; /* IE/Edge */
-}
-
-.element::-webkit-scrollbar {
-  display: none; /* Chrome/Safari/Opera */
-}
-</style>

+ 0 - 53
src/views/dataChart/index.vue

@@ -1,53 +0,0 @@
-<template>
-  <div class="pa-3 white">
-    <MTable
-      :headers="headers"
-      :items="items"
-      :show-select="false"
-      :loading="loading"
-      :can-delete="false"
-      @add="onAdd"
-    ></MTable>
-    <MDialog :visible.sync="show" title="图表编辑" fullscreen :footer="false">
-      <DataChartEdit v-if="show"></DataChartEdit>
-    </MDialog>
-  </div>
-</template>
-
-<script>
-import MTable from '@/components/List/table'
-import MDialog from '@/components/Dialog'
-import DataChartEdit from './dataChartEdit.vue'
-export default {
-  name: 'dataChart',
-  components: {
-    MTable,
-    MDialog,
-    DataChartEdit
-  },
-  data () {
-    return {
-      headers: [
-        { text: '标题', value: 'title' },
-        { text: '描述', value: 'describe' },
-        { text: '创建日期', value: 'createDate' },
-        { text: '操作', value: 'action' }
-      ],
-      items: [],
-      loading: false,
-      show: false
-    }
-  },
-  created () {
-  },
-  methods: {
-    onAdd () {
-      this.show = true
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-
-</style>

+ 0 - 106
src/views/dataChart/utils/options.js

@@ -1,106 +0,0 @@
-// const COLOR = ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00']
-
-export const line = {
-  title: '折线图',
-  icon: 'mdi-chart-line',
-  option: {
-    legend: {
-      show: true
-    },
-    xAxis: {
-      type: 'category',
-      data: []
-    },
-    yAxis: {
-      type: 'value'
-    },
-    series: [
-      {
-        data: [],
-        type: 'line',
-        smooth: true
-      }
-    ]
-  }
-}
-
-export const bar = {
-  title: '柱状图',
-  icon: 'mdi-chart-bar',
-  option: {
-    legend: {
-      show: true,
-      left: 'center'
-    },
-    xAxis: {
-      type: 'category',
-      data: []
-    },
-    yAxis: {
-      type: 'value'
-    },
-    series: [
-      {
-        data: [],
-        type: 'bar'
-      }
-    ]
-  }
-}
-
-export const pie = {
-  title: '饼图',
-  icon: 'mdi-chart-pie',
-  option: {
-    // title: {
-    //   text: 'Referer of a Website',
-    //   subtext: 'Fake Data',
-    //   left: 'center'
-    // },
-    tooltip: {
-      trigger: 'item'
-    },
-    legend: {
-      left: 'center'
-    },
-    series: [
-      {
-        name: 'Access From',
-        type: 'pie',
-        radius: '50%',
-        data: [],
-        emphasis: {
-          itemStyle: {
-            shadowBlur: 10,
-            shadowOffsetX: 0,
-            shadowColor: 'rgba(0, 0, 0, 0.5)'
-          }
-        }
-      }
-    ]
-  }
-}
-
-// // 散点
-// export const scatter = {
-//   title: '散点图',
-//   icon: 'mdi-chart-bubble',
-//   option: {
-//     legend: {
-//       show: true
-//     },
-//     xAxis: {
-//       type: 'category',
-//       data: []
-//     },
-//     yAxis: {
-//       type: 'value'
-//     },
-//     series: [
-//       {
-//         data: [],
-//         type: 'scatter'
-//       }
-//     ]
-//   }
-// }

+ 32 - 8
src/views/home/dataChartEditChat.vue

@@ -1,5 +1,6 @@
 <template>
-  <div class="chart-content-chat heightFull d-flex flex-column">
+  <div class="chart-content-chat heightFull d-flex flex-column position-relative overflow-hidden">
+    <slot></slot>
     <div class="chart-content-chat-title mb-3">
       <v-tabs>
         <v-tab>AI 取数</v-tab>
@@ -228,8 +229,7 @@
 <script>
 import {
   getAsk,
-  submitTrainingCorrect,
-  submitTrainingError
+  addFeedback
 } from '@/api/dataChart'
 import { mapGetters } from 'vuex'
 export default {
@@ -345,11 +345,12 @@ export default {
     },
     async onAddTrain (item, bool) {
       this.loading = true
-      const subApi = bool ? submitTrainingCorrect : submitTrainingError
       try {
-        await subApi({
+        await addFeedback({
           question: item.question,
-          sql: item.content.sql
+          sql: item.content.sql,
+          is_thumb_up: bool,
+          user_id: this.userInfo.id
         })
         item.dataValidation = true
         // this.$snackbar.success('操作成功')
@@ -366,10 +367,33 @@ export default {
       }
       const { typeAxis, dataAxis } = model
       const data = {
-        type: typeAxis ? content.rows.map(e => e[typeAxis]) : [],
-        data: dataAxis ? dataAxis.map(e => content.rows.map(r => r[e])) : []
+        type: typeAxis ? content.records.rows.map(e => e[typeAxis]) : [],
+        data: dataAxis ? dataAxis.map(e => content.records.rows.map(r => r[e])) : []
       }
       this.$emit('render', data)
+    },
+    update (data) {
+      this.items.splice(1, this.items.length - 1, ...data.messages.map(e => {
+        if (e.role === 'user') {
+          return {
+            type: 2,
+            user: '游客',
+            content: e.content
+          }
+        }
+        return {
+          type: 1,
+          content: e.metadata,
+          showSnackbar: false,
+          dataValidation: false,
+          question: e.content, // 记录当前问题
+          showMenu: false,
+          model: {
+            dataAxis: null,
+            typeAxis: null
+          }
+        }
+      }))
     }
   }
 }

+ 90 - 0
src/views/home/homeSide.vue

@@ -0,0 +1,90 @@
+<template>
+  <v-navigation-drawer
+    class="grey lighten-4 navigation"
+    dark
+    permanent
+  >
+    <template v-slot:prepend>
+      <v-list-item light>
+        <v-list-item-content>
+          <v-list-item-title class="text-h6">AI取数</v-list-item-title>
+          <!-- <v-list-item-subtitle>Logged In</v-list-item-subtitle> -->
+        </v-list-item-content>
+      </v-list-item>
+    </template>
+    <v-divider light></v-divider>
+    <v-list dense light>
+      <v-list-item-group
+        v-model="selected"
+        color="primary"
+        @change="onSelectConversation"
+      >
+        <v-list-item
+          v-for="(conversation) in conversationList"
+          :key="conversation.conversation_id"
+        >
+          <v-list-item-content>
+            <v-list-item-title>{{ conversation.conversation_title }}</v-list-item-title>
+          </v-list-item-content>
+        </v-list-item>
+      </v-list-item-group>
+    </v-list>
+
+    <template v-slot:append>
+      <div class="pa-2">
+        <v-btn block @click="handleLogout">
+          Logout
+        </v-btn>
+      </div>
+    </template>
+  </v-navigation-drawer>
+</template>
+
+<script>
+import {
+  getConversations,
+  getConversationsById
+} from '@/api/dataChart'
+export default {
+  name: 'homeSide',
+  data () {
+    return {
+      selected: null,
+      lastSelected: null,
+      conversationList: []
+    }
+  },
+  created () {
+    this.getConversationList()
+  },
+  methods: {
+    async getConversationList () {
+      try {
+        const { data } = await getConversations({ limit: 5 })
+        this.conversationList = data.conversations
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    },
+    async onSelectConversation (index) {
+      if (!index) {
+        setTimeout(() => {
+          this.selected = this.lastSelected
+        })
+        return
+      }
+      this.lastSelected = index
+      try {
+        const { data } = await getConversationsById(this.conversationList[index].conversation_id)
+        this.$emit('update', data)
+      } catch (error) {
+        this.$snackbar.error(error)
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+
+</style>

+ 45 - 35
src/views/home/index.vue

@@ -1,40 +1,43 @@
 <template>
-  <div class="d-flex chart heightFull widthFull pa-3">
-    <div v-if="showChart" class="chart-list heightFull overflow-y-auto mr-3">
-      <div v-for="(chart, key) in Charts" :key="key" class="chart-type mb-3" @click="onChange(key)">
-        <div>
-          <span class="mdi" :class="chart.icon"></span>
-        </div>
-        <div>
-          {{ chart.title }}
+  <div class="heightFull widthFull d-flex">
+    <HomeSide @update="onUpdate"></HomeSide>
+    <div class="d-flex chart heightFull widthFull pa-3">
+      <div v-if="showChart" class="chart-list heightFull overflow-y-auto mr-3">
+        <div v-for="(chart, key) in Charts" :key="key" class="chart-type mb-3" @click="onChange(key)">
+          <div>
+            <span class="mdi" :class="chart.icon"></span>
+          </div>
+          <div>
+            {{ chart.title }}
+          </div>
         </div>
       </div>
-    </div>
 
-    <div class="chart-content d-flex heightFull">
-      <FullscreenToggle v-if="showChart" class="chart-content-show heightFull white mr-3 overflow-hidden pa-3 position-relative" ref="box">
-        <template v-slot="{ toggle, isFullscreen }">
-          <div class="position-absolute d-flex flex-column" style="right: 10px; top: 80px; z-index: 999">
-            <v-btn
-              icon
-              color="primary"
-              @click="toggle"
-            >
-              <v-icon>{{ isFullscreen ? 'mdi-arrow-collapse' : 'mdi-arrow-expand'}}</v-icon>
-            </v-btn>
-            <v-btn
-              icon
-              color="primary"
-              class="mt-3"
-              @click="onClose"
-            >
-              <v-icon>mdi-close</v-icon>
-            </v-btn>
-          </div>
-          <InitChart ref="chart" class="heightFull widthFull"></InitChart>
-        </template>
-      </FullscreenToggle>
-      <DataChartEditChat :class="showChart ? 'widthHalf' : 'widthFull'" @render="onRender"></DataChartEditChat>
+      <div class="chart-content d-flex heightFull">
+        <FullscreenToggle v-if="showChart" class="chart-content-show heightFull white mr-3 overflow-hidden pa-3 position-relative" ref="box">
+          <template v-slot="{ toggle, isFullscreen }">
+            <div class="position-absolute d-flex flex-column" style="right: 10px; top: 80px; z-index: 999">
+              <v-btn
+                icon
+                color="primary"
+                @click="toggle"
+              >
+                <v-icon>{{ isFullscreen ? 'mdi-arrow-collapse' : 'mdi-arrow-expand'}}</v-icon>
+              </v-btn>
+              <v-btn
+                icon
+                color="primary"
+                class="mt-3"
+                @click="onClose"
+              >
+                <v-icon>mdi-close</v-icon>
+              </v-btn>
+            </div>
+            <InitChart ref="chart" class="heightFull widthFull"></InitChart>
+          </template>
+        </FullscreenToggle>
+        <DataChartEditChat ref="dataChartEditChatRefs" :class="showChart ? 'widthHalf' : 'widthFull'" @render="onRender"></DataChartEditChat>
+      </div>
     </div>
   </div>
 </template>
@@ -45,14 +48,15 @@ import * as Charts from './utils/options.js'
 import DataChartEditChat from './dataChartEditChat.vue'
 import InitChart from '@/components/Charts/initChart'
 import { cloneDeep } from 'lodash'
-
+import HomeSide from './homeSide.vue'
 import FullscreenToggle from '@/components/FullscreenToggle'
 export default {
   name: 'HomeIndex',
   components: {
     DataChartEditChat,
     InitChart,
-    FullscreenToggle
+    FullscreenToggle,
+    HomeSide
   },
   data () {
     return {
@@ -121,6 +125,12 @@ export default {
       }
       this.chart.setOption(_option, true)
       this.chart.hideLoading()
+    },
+    handleLogout () {
+      this.$store.dispatch('user/userLogout')
+    },
+    onUpdate (data) {
+      this.$refs.dataChartEditChatRefs.update(data)
     }
   }
 }