Jing před 4 měsíci
rodič
revize
8c8bdd58e3

+ 1 - 1
.nuxt/manifest/latest.json

@@ -1 +1 @@
-{"id":"dev","timestamp":1733384844545}
+{"id":"dev","timestamp":1733876490845}

+ 1 - 1
.nuxt/manifest/meta/dev.json

@@ -1 +1 @@
-{"id":"dev","timestamp":1733384844545,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}
+{"id":"dev","timestamp":1733876490845,"matcher":{"static":{},"wildcard":{},"dynamic":{}},"prerendered":[]}

+ 3 - 3
.nuxt/nitro.json

@@ -1,5 +1,5 @@
 {
-  "date": "2024-12-05T07:47:36.548Z",
+  "date": "2024-12-11T00:21:40.998Z",
   "preset": "nitro-dev",
   "framework": {
     "name": "nuxt",
@@ -9,9 +9,9 @@
     "nitro": "2.9.7"
   },
   "dev": {
-    "pid": 12700,
+    "pid": 21260,
     "workerAddress": {
-      "socketPath": "\\\\.\\pipe\\nitro\\worker-12700-1.sock"
+      "socketPath": "\\\\.\\pipe\\nitro\\worker-21260-1.sock"
     }
   }
 }

+ 22 - 0
api/api.js

@@ -0,0 +1,22 @@
+const url = 'http://192.168.1.127:9501/'
+const token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwaHBlcjY2Ni9qd3QiLCJ1aWQiOjczLCJ1c2VyX25hbWUiOiIxNTIxMDIxMTIwMCIsIm1vYmlsZSI6IjE1MjEwMjExMjAwIiwiZW1haWwiOiIxNTIxMDIxMTIwMEBxcS5jb20iLCJsZXZlbF9pZCI6MSwidHlwZV9pZCI6MTAwMDAsImp3dF9zY2VuZSI6ImRlZmF1bHQiLCJqdGkiOiJkZWZhdWx0XzY3NTc5ZDczZDM2OWEzLjYwNjIxMTc3IiwiaWF0IjoxNzMzNzk1MTg3LCJuYmYiOjE3MzM3OTUxODcsImV4cCI6MTczMzg4MTU4N30.bGS53Hzom42M12DVgpZKR7BugBRuQpLnha-CCtHi_to'
+import { useFetch } from '#app'
+
+const request = async (route, getData) => {
+    return new Promise(async (resolve, reject) => {
+        try {
+            const {data: fetchedData} = await useFetch(url + route, {
+                method: 'POST',
+                body: getData,
+                headers: {
+                    'token': `${token}`
+                }
+            });
+            resolve(fetchedData);
+        } catch (error) {
+            reject(error);
+        }
+    });
+}
+
+export default request;

+ 8 - 3
components/home/pageHead.vue

@@ -49,18 +49,23 @@
                 </div>
                 <ul class="serve">
                     <li>
-                        <NuxtLink to="">
+                        <NuxtLink to="/login">
                             <img src="../../static/images/huiyuan 1.png" alt="">
                             <p>会员服务</p>
                         </NuxtLink>
                     </li>
                     <li>
-                        <!-- @click="goAdvertising" -->
+                        <NuxtLink to="/topic">
+                            <img src="../../static/images/huiyuan 1.png" alt="">
+                            <p>商圈</p>
+                        </NuxtLink>
+                    </li>
+                    <!-- <li>
                         <NuxtLink target="_blank" to="/advertising">
                             <img src="../../static/images/lingquguanggao 1.png" alt="">
                             <p>广告服务</p>
                         </NuxtLink>
-                    </li>
+                    </li> -->
                 </ul>
             </div>
         </div>

+ 20 - 0
package-lock.json

@@ -15,6 +15,7 @@
         "add": "^2.0.6",
         "axios": "^1.7.7",
         "element-plus": "^2.8.5",
+        "eventsource": "^3.0.1",
         "file-saver": "2.0.5",
         "js-cookie": "^3.0.5",
         "less": "^4.2.0",
@@ -5668,6 +5669,25 @@
         "node": ">=0.8.x"
       }
     },
+    "node_modules/eventsource": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-3.0.1.tgz",
+      "integrity": "sha512-tyGtsrTc9fi+N5qFU6G2MLjcBbsdCOQ/QE9Cc96Mt6q02YkQrIJGOaNMg6qiXRJDzxecN7BntJYNRE/j0OIhMQ==",
+      "dependencies": {
+        "eventsource-parser": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/eventsource-parser": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/eventsource-parser/-/eventsource-parser-3.0.0.tgz",
+      "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==",
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
     "node_modules/execa": {
       "version": "7.2.0",
       "resolved": "https://registry.npmmirror.com/execa/-/execa-7.2.0.tgz",

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "add": "^2.0.6",
     "axios": "^1.7.7",
     "element-plus": "^2.8.5",
+    "eventsource": "^3.0.1",
     "file-saver": "2.0.5",
     "js-cookie": "^3.0.5",
     "less": "^4.2.0",

+ 14 - 11
pages/advertising/index.vue

@@ -53,7 +53,6 @@
                                     <span v-show="!adName">请输入广告名称。</span>
                                 </p>
                                 <p>
-
                                     <label for="adLink">广告链接:</label>
                                     <el-input class="adLink" v-model="adLink" style="width: 980px;height: 44px;"
                                         placeholder="请输入广告链接" />
@@ -83,25 +82,26 @@
                                 </p>
                                 <p>
                                     <label>广告图:</label>
-                                    <el-upload class="avatar-uploader"
-                                        action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
-                                        :show-file-list="false" :on-success="handleAvatarSuccess"
+                                    <!-- <el-upload class="avatar-uploader"
+                                        action="http://192.168.1.118:9501"
+                                        :show-file-list="false" 
+                                        :on-success="handleAvatarSuccess"
                                         :before-upload="beforeAvatarUpload">
                                         <img v-if="imageUrl" :src="imageUrl" class="avatar" />
                                         <el-icon v-else class="avatar-uploader-icon">
                                             <Plus />
                                         </el-icon>
-                                    </el-upload>
-                                    <!-- <el-upload class="upload-demo" action="http://192.168.1.118:9501"
-                                        :on-success="handleUploadSuccess" :on-error="handleUploadError" drag multiple>
+                                    </el-upload> -->
+                                    <el-upload class="avatar-uploader" action="http://192.168.1.118:9501"
+                                        :on-success="handleUploadSuccess" :before-upload="beforeAvatarUpload">
                                         <el-icon class="el-icon--upload">
                                             <Plus />
                                         </el-icon>
                                         <div class="el-upload__text">
                                             选择图片
                                         </div>
-                                    </el-upload> -->
-                                    <!-- <span >请添加广告图,不大于500k。</span> -->
+                                    </el-upload>
+                                    <span >请添加广告图,不大于500k。</span>
                                 </p>
                             </div>
 
@@ -256,6 +256,10 @@ let handleUploadSuccess = (response, file, fileList) => {
     // console.log('上传成功,文件地址为:', fileUrl);
 }
 
+let beforeAvatarUpload =()=>{
+    
+}
+
 
 //选择的广告位
 let handleSelectionChange = (selectedRows) => {
@@ -419,7 +423,6 @@ let nextStep = () => {
                 item.size = item.width + ' * ' + item.height + ' px '
             }
             console.log(tableData.value);
-
         }).catch(error => {
             console.log(error);
         })
@@ -460,7 +463,6 @@ let nextStep = () => {
             number[2].classList.add('bgc')
             title[2].classList.add('color')
 
-
         }).catch(error => {
             console.log(error);
         })
@@ -696,6 +698,7 @@ watch(adSize, (newval) => {
                                 height: 178px;
                                 text-align: center;
                             }
+
                             //上传图片相关样式 end
 
 

+ 238 - 8
pages/login.vue

@@ -1,16 +1,246 @@
-<template>
-  <!-- 登录页面 -->
+<!-- <template>
   <div class="login">
-    <h1>登录</h1>
+    <h1></h1>
 
   </div>
 </template>
 
 <script setup>
-import { setToken } from '@/store/useCookieStore'
+import { onMounted, onUnmounted } from 'vue';
+// import { EventSource } from 'eventsource'
+import EventSource from 'eventsource';
+
+// 创建 EventSource 实例,订阅事件流
+// const eventSource = new EventSource('http://192.168.126.42/gap/nodes');
+// const EventSource = require('eventsource');
+// var EventSource = {};
+// var myCustomVariable = {};
+const eventSource = new EventSource('http://192.168.1.201:9501/collector/zhipu');
+
+
+const express = require('express');
+const app = express();
+
+app.get('/events', (req, res) => {
+  res.setHeader('Content-Type', 'text/event-stream');
+
+  // 这里可以编写逻辑来持续推送事件数据,例如
+  const interval = setInterval(() => {
+    res.write('data: Some data here\n\n');
+  }, 1000);
+
+  req.on('close', () => {
+    clearInterval(interval);
+    res.end();
+  });
+
+});
+
+app.listen(3000, () => {
+  console.log('Server started on port 3000');
+});
+
+onUnmounted(() => {
+  eventSource.close();
+});
+
+// 页面加载时
+onMounted(() => {
+  // getTableData();
+  // 创建 EventSource 实例,订阅事件流
+  eventSource.onmessage = (event) => {
+
+    console.log(event);
+
+    // 处理收到的数据
+    console.log(JSON.parse(event.data));
+  };
+  eventSource.onerror = (event) => {
+    // 处理错误
+  };
+});
+
+</script> -->
+
+
+<!-- 
+<template>
+  <div>
+    <div v-for="(message, index) in messages" :key="index">{{ message }}</div>
+  </div>
+</template>
+
+<script>
+import { ref } from 'vue';
+import axios from 'axios';
+
+export default {
+  setup() {
+    const messages = ref([]);
+
+    const eventSource = axios.create({
+      baseURL: 'http://192.168.1.201:9501',
+      headers: { 'Content-Type': 'text/event-stream' }
+    });
+
+    const setupEventSource = () => {
+      const source = new EventSource(eventSource.getUri({ path: '/collector/zhipu' }));
+
+      source.onmessage = (event) => {
+        const data = JSON.parse(event.data);
+        messages.value.push(data.message);
+      };
+
+      source.onerror = (error) => {
+        console.error(error);
+      };
+    };
+
+    setupEventSource();
+
+    return { messages };
+  }
+};
+</script> -->
+
+
+
+<!-- <template>
+  <div>
+    <button @click="startEventSource">开始接收事件</button>
+    <button @click="stopEventSource">停止接收事件</button>
+    <div v-for="event in events" :key="event.id">
+      {{ event.data }}
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'EventSourceExample',
+  data() {
+    return {
+      events: [],
+      eventSource: null
+    };
+  },
+  methods: {
+    startEventSource() {
+      if (!this.eventSource) {
+        this.eventSource = new EventSource('http://192.168.1.201:9501/collector/zhipu');
+        this.eventSource.onmessage = this.handleMessage;
+        this.eventSource.onerror = this.handleError;
+      }
+    },
+    stopEventSource() {
+      if (this.eventSource) {
+        this.eventSource.close();
+        this.eventSource = null;
+      }
+    },
+    handleMessage(event) {
+      // 处理接收到的消息
+      console.log('收到消息:', event.data);
+      this.events.push({ id: this.events.length, data: event.data });
+    },
+    handleError(event) {
+      // 处理错误
+      console.error('EventSource 错误:', event);
+      this.stopEventSource();
+    }
+  },
+  beforeDestroy() {
+    // 组件销毁前停止 EventSource
+    this.stopEventSource();
+  }
+};
+</script> -->
+
+<template>
+  <div>
+    <h2>事件流数据</h2>
+    <ul>
+      <li v-for="(item, index) in dataStream" :key="index">{{ item }}</li>
+    </ul>
+    <button @click="startStream">启动事件流</button>
+    <button @click="stopStream">停止事件流</button>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      dataStream: [], // 用于存储从事件流接收的数据
+      eventSource: null, // 保存 EventSource 实例
+    };
+  },
+  methods: {
+    // 启动事件流
+    startStream() {
+      if (this.eventSource) {
+        console.log("事件流已经启动!");
+        return;
+      }
+
+      // 连接到 PHP SSE 后端接口
+      // const apiUrl = "http://192.168.1.201:8989/";
+      const apiUrl = "http://192.168.1.201:9501/stream";
+      this.eventSource = new EventSource(apiUrl);
+
+      // 监听普通消息
+      this.eventSource.onmessage = (event) => {
+        console.log(event);
+
+        console.log("收到事件流数据:", event.data);
+        this.dataStream.push(JSON.parse(event.data)); // 将数据存储到 dataStream 中
+      };
+
+      // 监听自定义事件(如关闭事件)
+      this.eventSource.addEventListener("close", (event) => {
+        console.log("事件流关闭:", event.data);
+        this.stopStream(); // 停止事件流
+      });
+
+      // 监听错误事件
+      this.eventSource.onerror = (error) => {
+        console.error("事件流错误:", error);
+        this.stopStream(); // 遇到错误时停止事件流
+      };
+    },
+
+    // 停止事件流
+    stopStream() {
+      if (this.eventSource) {
+        this.eventSource.close(); // 关闭事件流
+        this.eventSource = null;
+        console.log("事件流已停止");
+      }
+    },
+  },
+  beforeDestroy() {
+    // 在组件销毁前关闭事件流
+    this.stopStream();
+  },
+};
+</script>
+
+<style scoped>
+h2 {
+  color: #333;
+}
 
-//存储token
-let token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJwaHBlcjY2Ni9qd3QiLCJ1aWQiOjY1LCJ1c2VyX25hbWUiOiIxMSIsInJvbGVfaWQiOjAsIm1vYmlsZSI6IjE1ODY2NjY2NjY1IiwiZW1haWwiOiIxMjNlQHFxLmNvbSIsInJvbmdfdG9rZW4iOiIiLCJsZXZlbF9pZCI6MSwiand0X3NjZW5lIjoiZGVmYXVsdCIsImp0aSI6ImRlZmF1bHRfNjcxNjBlYmMxOTJlZTUuODQyNDQzODUiLCJpYXQiOjE3Mjk0OTg4MTIsIm5iZiI6MTcyOTQ5ODgxMiwiZXhwIjoxNzMyMDkwODEyfQ.Xu_TpUc75iE4h7iPRe5J_gQ8_rRPBHvFINCQR2pEuKk"
-setToken(token)
+button {
+  margin: 5px;
+  padding: 10px;
+  background-color: #007bff;
+  color: white;
+  border: none;
+  border-radius: 5px;
+  cursor: pointer;
+}
 
-</script>
+button:hover {
+  background-color: #0056b3;
+}
+</style>

+ 53 - 0
pages/topic/[id].vue

@@ -0,0 +1,53 @@
+<template>
+    <div>
+        <!-- 页面头部 -->
+        <!-- <HomePageHead></HomePageHead> -->
+        <AdvertisingHead></AdvertisingHead>
+        <!-- 商圈详情 -->
+        <div class="topicInfoBox">
+            {{ dataInfo }}
+        </div>
+        <!-- 页面底部 -->
+        <!-- <HomeFoot></HomeFoot> -->
+        <AdvertisingFoot></AdvertisingFoot>
+    </div>
+</template>
+
+<script setup>
+//1.引用模块 start ---------------------------------------->
+//使用ref和reactive动态变量
+import { ref, reactive, onMounted } from 'vue'
+import { useRoute } from 'vue-router';
+//使用官方ssr请求模块
+import { useNuxtApp, useFetch } from '#app'
+//使用element-plus组件
+import { ElTabs, ElTabPane, ElTable, ElTableColumn, ElPagination } from 'element-plus';
+import request from '@/api/api';
+//1.引用模块 end ---------------------------------------->
+
+//2.页面数据 start ---------------------------------------->
+const dataInfo = ref({})
+//2.页面数据 end ---------------------------------------->
+
+
+//2.获取商圈详情 start ---------------------------------------->
+const route = useRoute();
+const id = route.params.id; // 获取传递的 id 参数
+
+const getTopicInfo = () => {
+    request('chat/getTopicInfo', { id: id }).then(res => {
+        dataInfo.value = res._rawValue.data.data;
+    })
+}
+getTopicInfo();
+console.log(dataInfo)
+//2.获取商圈详情 end ---------------------------------------->
+</script>
+
+<style lang="less" scoped>
+.topicInfoBox {
+    width: 1200px;
+    margin: 0 auto;
+    padding: 20px 0;
+}
+</style>

+ 126 - 0
pages/topic/index.vue

@@ -0,0 +1,126 @@
+<template>
+    <div>
+        <!-- 页面头部 -->
+         <AdvertisingHead></AdvertisingHead>
+        <div class="topicBox">
+            <el-tabs v-model="activeName" class="demo-tabs" @tab-click="getTopicsList">
+                <el-tab-pane :label="item.label" :name="item.value" v-for="item in typeList" >
+                    <div class="topicList">
+                        
+                    </div>
+                    <!-- <div class="topicInner">
+                        <el-table :data="listData" border row-key="id">
+                            <el-table-column prop="title" label="话题"/>
+                            <el-table-column prop="user_name" label="作者"/>
+                            <el-table-column label="状态">
+                                <template #default="{row}">
+                                    <span v-if="row.status == '1'" style="color: #666;">未审核</span>
+                                    <span v-if="row.status == '2'" style="color: green;">已通过</span>
+                                    <span v-if="row.status == '3'" style="color: red;">已拒绝</span>
+                                </template>
+                            </el-table-column>
+                            <el-table-column prop="created_at" label="创建时间"/>
+                            <el-table-column prop="updated_at" label="更新时间"/>
+                            <el-table-column label="操作">
+                                <template #default="{ row }">
+                                    <NuxtLink :to="`/topic/${row.id}`">详情</NuxtLink>
+                                </template>
+                            </el-table-column>
+                        </el-table>
+                    </div> -->
+                    <div class="paginationBox">
+                        <el-pagination background layout="prev, pager, next" :total="currentPage" />
+                    </div>
+                </el-tab-pane>
+            </el-tabs>
+        </div>
+        <!-- 页面底部 -->
+        <AdvertisingFoot></AdvertisingFoot>
+    </div>
+</template>
+
+<script setup>
+    //1.引用模块 start ---------------------------------------->
+    //使用ref和reactive动态变量
+    import { ref, reactive,onMounted  } from 'vue'
+    //使用官方ssr请求模块
+    import { useNuxtApp,useFetch } from '#app'
+    //使用element-plus组件
+    import { ElTabs, ElTabPane, ElTable, ElTableColumn, ElPagination } from 'element-plus';
+    import request from '@/api/api';
+    //1.引用模块 end ---------------------------------------->
+
+    //2.页面数据 start ---------------------------------------->
+    const activeName = ref(1)
+    const listData = ref([])//商圈列表
+    const typeList= ref([])//商圈分类
+    const statusList = ref([])//商圈状态
+    const currentPage = ref(0)
+    //2.页面数据 end ---------------------------------------->
+
+    //3.请求商圈列表 start ---------------------------------------->
+    const searchApi = reactive({
+        title:"",//标题
+        type:"",//课题分类
+        nickname:"",//作者
+        status:"",//审核状态
+        page:1,//当前是第几页
+        page_size:10,//一共多少条
+    });
+    const getTopicsList = () => {
+        searchApi.type = activeName;
+        
+        request('chat/getTopicsList',searchApi).then(res=>{
+            listData.value = res._rawValue.data.data;
+            currentPage.value = res._rawValue.data.total;
+        })
+    }
+    getTopicsList();
+    //3.请求商圈列表 end ---------------------------------------->
+
+    
+    //4.获取分类和状态 start ---------------------------------------->
+    const topicType = () => {
+        request('chat/topicType').then(res=>{
+            typeList.value = res._rawValue.data;
+        })
+    }
+    topicType();
+  
+    const topicStatus = () => {
+        request('chat/topicStatus').then(res=>{
+            statusList.value = res._rawValue.data;
+        })
+    }
+    topicStatus();
+    //4.获取分类和状态 end ---------------------------------------->
+
+
+    //回复商圈
+    // axios.post(`/chat/addReply`).then(response=>{
+    //     // console.log(response.data.rows);
+    //     newsList.value = response.data.rows;
+    // }).catch(error => {
+    //     console.error(error);
+    // })
+
+</script>
+
+<style lang="less" scoped>
+    .demo-tabs > .el-tabs__content {
+        padding: 32px;
+        color: #6b778c;
+        font-size: 32px;
+        font-weight: 600;
+    }
+    .topicBox {
+        width: 1200px;
+        margin: 40px auto;
+       
+    }
+    .paginationBox {
+        display: flex;
+        justify-content: center;
+        margin-top: 20px;
+    }
+</style>