diff --git a/泛读报告.docx b/泛读报告.docx
new file mode 100644
index 0000000..0e9e7ae
Binary files /dev/null and b/泛读报告.docx differ
diff --git a/注释(1) b/注释(1)
new file mode 100644
index 0000000..1ea6a14
--- /dev/null
+++ b/注释(1)
@@ -0,0 +1,296 @@
+poetizepoetize-im-uisrcmain.js
+// 引入Vue 3的createApp方法,用于创建Vue应用实例
+import { createApp } from 'vue';
+
+// 引入应用的根组件
+import App from './App.vue';
+
+// 引入Vue Router实例,用于管理前端路由
+import router from './router';
+
+// 引入Vuex或Pinia实例,用于状态管理
+import store from './store';
+
+// 从naive-ui库中引入多个UI组件,并使用create方法创建一个Naive UI插件实例
+import {
+ create,
+ NAvatar,
+ NInput,
+ NIcon,
+ NTag,
+ NDivider,
+ NButton,
+ NDrawer,
+ NCard,
+ NTabs,
+ NTabPane,
+ NSwitch,
+ NModal,
+ NBadge,
+ NPopover,
+ NImage,
+ NPopconfirm
+} from 'naive-ui';
+
+poetizepoetize-im-uisrcApp.vue
+// 从element-plus库中引入特定组件,并引入其样式文件
+import {
+ ElUpload,
+ ElButton,
+ ElRadioGroup,
+ ElRadioButton
+} from 'element-plus';
+import 'element-plus/dist/index.css';
+
+// 引入自定义的HTTP请求工具、通用方法和常量
+import http from './utils/request';
+import common from './utils/common';
+import constant from './utils/constant';
+
+// 引入自定义字体和样式文件
+import 'vfonts/FiraCode.css';
+import './assets/css/index.css';
+import './assets/css/color.css';
+import './assets/css/animation.css';
+
+// 创建一个Naive UI插件实例,并注册所有需要的组件
+const naive = create({
+ components: [
+ NAvatar, NInput, NIcon, NTag, NDivider, NButton,
+ NDrawer, NCard, NTabs, NTabPane, NSwitch, NModal,
+ NBadge, NPopover, NImage, NPopconfirm
+ ]
+});
+
+// 创建Vue应用实例
+const app = createApp(App);
+
+// 使用Vue Router和状态管理实例
+app.use(router);
+app.use(store);
+
+// 使用Naive UI插件
+app.use(naive);
+
+// 单独注册Element Plus的组件,使它们可以在应用的全局范围内使用
+app.component(ElUpload.name, ElUpload);
+app.component(ElButton.name, ElButton);
+app.component(ElRadioGroup.name, ElRadioGroup);
+app.component(ElRadioButton.name, ElRadioButton);
+
+// 添加全局属性,用于HTTP请求、通用方法和常量
+app.config.globalProperties.$http = http;
+app.config.globalProperties.$common = common;
+app.config.globalProperties.$constant = constant;
+
+// 添加全局前置路由守卫
+router.beforeEach((to, from, next) => {
+ // 检查目标路由是否需要认证
+ if (to.meta.requiresAuth) {
+ // 如果是根路径,则检查URL查询参数
+ if (to.path === "/") {
+ // 如果存在defaultStoreType查询参数,则存储到localStorage
+ if (typeof to.query.defaultStoreType !== "undefined") {
+ localStorage.setItem("defaultStoreType", to.query.defaultStoreType);
+ }
+ // 如果存在userToken查询参数,则验证token
+ if (typeof to.query.userToken !== "undefined") {
+ let userToken = to.query.userToken;
+ const xhr = new XMLHttpRequest();
+ xhr.open('post', constant.baseURL + "/user/token", false); // 同步请求,不推荐在生产环境中使用
+ xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ xhr.send("userToken=" + userToken);
+ let result = JSON.parse(xhr.responseText);
+ // 如果token验证通过,则加载用户信息并重定向到应用主页面
+ if (!common.isEmpty(result) && result.code === 200) {
+ store.commit("loadCurrentUser", result.data);
+ localStorage.setItem("userToken", result.data.accessToken);
+ window.location.href = constant.imURL;
+ next();
+ } else {
+ // 如果token验证失败,则重定向到登录页面
+ window.location.href = constant.webBaseURL;
+ }
+ } else if (Boolean(localStorage.getItem("userToken"))) {
+ // 如果已存储userToken,则允许路由变化
+ next();
+ } else {
+ // 如果没有userToken,则重定向到登录页面
+ window.location.href = constant.webBaseURL;
+ }
+ } else {
+ // 如果不是根路径,则检查是否已存储userToken
+ if (Boolean(localStorage.getItem("userToken"))) {
+ // 如果已存储userToken,则允许路由变化
+ next();
+ } else {
+ // 如果没有userToken,则重定向到登录页面
+ window.location.href = constant.webBaseURL;
+ }
+ }
+ } else {
+ // 如果目标路由不需要认证,则直接允许路由变化
+ next();
+ }
+});
+
+// 将Vue应用挂载到HTML中的#app元素上
+app.mount('#app');
+
+poetizepoetize-im-uisrcutilsajaxUpload.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// 从当前目录下的common.js文件中导入common模块(可能包含一些工具函数或配置)
+import common from './common';
+
+// 定义一个函数,用于根据XMLHttpRequest对象生成一个错误对象
+function getError(action, option, xhr) {
+ let msg; // 定义一个变量用于存储错误信息
+
+ // 如果xhr对象有response属性(通常用于处理响应体为JSON的情况)
+ if (xhr.response) {
+ // 使用xhr.response.error(如果存在)或xhr.response作为错误信息
+ msg = `${xhr.response.error || xhr.response}`;
+ }
+ // 如果xhr对象有responseText属性(用于处理文本响应)
+ else if (xhr.responseText) {
+ // 直接使用xhr.responseText作为错误信息
+ msg = `${xhr.responseText}`;
+ }
+ // 如果以上两种情况都不满足
+ else {
+ // 使用一个通用的错误信息,包括失败的动作和HTTP状态码
+ msg = `fail to ${action} ${xhr.status}`;
+ }
+
+ // 返回一个包含错误信息的Error对象
+ return new Error(msg);
+}
+
+// 定义一个函数,用于获取XMLHttpRequest对象的响应体
+function getBody(xhr) {
+ const text = xhr.responseText || xhr.response; // 尝试获取responseText或response属性
+
+ // 如果没有响应文本,则直接返回
+ if (!text) {
+ return text;
+ }
+
+ // 尝试将文本解析为JSON对象
+ try {
+ return JSON.parse(text);
+ } catch {
+ // 如果解析失败,则返回原始的文本
+ return text;
+ }
+}
+
+// 导出默认函数,该函数用于发送一个XMLHttpRequest请求
+export default function (option) {
+ const xhr = new XMLHttpRequest(); // 创建一个新的XMLHttpRequest对象
+ const action = option.action; // 从option对象中获取请求的URL
+
+ const formData = new FormData(); // 创建一个FormData对象用于存储要发送的数据
+
+ // 如果option对象中有data属性,则遍历其键值对并添加到formData中
+ if (option.data) {
+ for (const [key, value] of Object.entries(option.data)) {
+ // 如果值是数组,则使用...操作符将其展开为多个参数传递给append方法
+ if (Array.isArray(value)) formData.append(key, ...value);
+ else formData.append(key, value);
+ }
+ }
+
+ // 添加文件数据到formData中,文件名和文件对象从option中获取
+ formData.append(option.filename, option.file, option.file.name);
+
+ // 监听错误事件,并在错误发生时调用option.onError函数,传入一个Error对象
+ xhr.addEventListener('error', () => {
+ option.onError(getError(action, option, xhr));
+ });
+
+ // 监听加载完成事件,并根据响应状态码调用相应的回调函数
+ xhr.addEventListener('load', () => {
+ // 如果响应状态码表示错误(不是2xx)
+ if (xhr.status < 200 || xhr.status >= 300) {
+ // 调用option.onError函数,传入一个Error对象
+ return option.onError(getError(action, option, xhr));
+ }
+ // 如果响应成功,则调用option.onSuccess函数,并传入响应体
+ option.onSuccess(getBody(xhr));
+ });
+
+ // 打开请求,指定方法(如GET、POST)、URL和是否异步
+ xhr.open(option.method, action, true);
+
+ // 如果option对象中有withCredentials属性且xhr对象支持该属性,则设置withCredentials为true
+ if (option.withCredentials && 'withCredentials' in xhr) {
+ xhr.withCredentials = true;
+ }
+
+ // 设置请求头,如果option对象中有headers属性
+ const headers = option.headers || {};
+ if (headers instanceof Headers) {
+ // 如果headers是一个Headers对象,则遍历其键值对并设置到xhr中
+ headers.forEach((value, key) => xhr.setRequestHeader(key, value));
+ } else {
+ // 否则,遍历一个普通的对象并设置请求头,跳过空值
+ for (const [key, value] of Object.entries(headers)) {
+ if (common.isEmpty(value)) continue; // 使用common模块的isEmpty函数检查值是否为空
+ xhr.setRequestHeader(key, value);
+ }
+ }
+
+ // 发送请求,并传入formData作为请求体
+ xhr.send(formData);
+
+ // 返回xhr对象,允许调用者进一步操作(如取消请求)
+ return xhr;
+}
\ No newline at end of file
diff --git a/注释(2) b/注释(2)
new file mode 100644
index 0000000..03fd510
--- /dev/null
+++ b/注释(2)
@@ -0,0 +1,200 @@
+poetizepoetize-im-uibabel.config.js
+
+// 这是一个使用CommonJS模块规范导出的对象,常见于Node.js环境或Babel配置文件中
+module.exports = {
+ // presets数组定义了一系列Babel预设(preset),这些预设是Babel插件的集合
+ // 预设可以帮助简化Babel的配置,因为它们已经预定义了一组插件和它们的配置
+ presets: [
+ // '@vue/cli-plugin-babel/preset' 是一个特定的预设,它是由Vue CLI的Babel插件提供的
+ // 这个预设会自动配置Babel以支持Vue.js项目中的现代JavaScript特性
+ // 它还包括了对Vue文件(.vue文件)中单文件组件(SFCs)的支持
+ '@vue/cli-plugin-babel/preset'
+ ]
+}
+
+
+
+poetizepoetize-im-ui.eslintrc.js
+
+// 使用CommonJS模块规范导出配置对象
+module.exports = {
+ // 设置为true表示这是一个ESLint配置文件的根文件,会停止在父级目录中查找配置文件
+ root: true,
+
+ // 指定代码的运行环境,这里设置为node,表示代码将在Node.js环境中运行
+ // 这有助于ESLint识别Node.js全局变量和Node.js特有的语法
+ env: {
+ node: true
+ },
+
+ // 继承(extends)一个或多个配置文件,这里继承了两个:
+ // 1. 'plugin:vue/vue3-essential':这是Vue.js官方ESLint插件提供的针对Vue 3的基本规则集
+ // 2. '@vue/standard':这是Vue.js官方提供的标准规则集,它基于ESLint的标准规则集并做了一些Vue特有的调整
+ extends: [
+ 'plugin:vue/vue3-essential',
+ '@vue/standard'
+ ],
+
+ // 解析器选项,这里指定了使用'babel-eslint'作为解析器
+ // 'babel-eslint'允许你使用Babel转译JavaScript代码的ESLint,这在你使用Babel特定功能时非常有用
+ // 注意:从ESLint 6.0.0开始,官方推荐使用'@babel/eslint-parser',因为'babel-eslint'已经逐渐停止维护
+ // 但如果你的项目还在使用旧版本的ESLint或Babel,这里仍然可以使用'babel-eslint'
+ parserOptions: {
+ parser: 'babel-eslint'
+ },
+
+ // 自定义规则,这里定义了两个规则:
+ // 1. 'no-console':控制是否允许使用console语句
+ // 如果当前环境变量NODE_ENV为'production',则设置为'warn',表示在控制台给出警告但不阻止代码执行
+ // 否则设置为'off',表示完全允许使用console语句
+ // 2. 'no-debugger':控制是否允许使用debugger语句
+ // 如果当前环境变量NODE_ENV为'production',则设置为'warn',表示在控制台给出警告但不阻止代码执行
+ // 否则设置为'off',表示完全允许使用debugger语句
+ rules: {
+ 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off'
+ }
+}
+
+
+
+poetizepoetize-im-uisrcmain.js
+
+// 从vue包中导入createApp方法,用于创建Vue应用实例
+import { createApp } from 'vue';
+
+// 导入根组件App.vue
+import App from './App.vue';
+
+// 导入路由配置
+import router from './router';
+
+// 导入Vuex状态管理库的配置
+import store from './store';
+
+// 从naive-ui包中导入一系列UI组件和create方法,用于创建naive-ui的Vue插件实例
+import {
+ create,
+ NAvatar,
+ NInput,
+ NIcon,
+ NTag,
+ NDivider,
+ NButton,
+ NDrawer,
+ NCard,
+ NTabs,
+ NTabPane,
+ NSwitch,
+ NModal,
+ NBadge,
+ NPopover,
+ NImage,
+ NPopconfirm
+} from 'naive-ui';
+
+// 从element-plus包中导入部分UI组件
+import {
+ ElUpload,
+ ElButton,
+ ElRadioGroup,
+ ElRadioButton
+} from 'element-plus';
+
+// 导入element-plus的样式文件
+import 'element-plus/dist/index.css';
+
+// 导入其他工具模块和样式文件
+import http from './utils/request'; // 用于发送HTTP请求的封装函数
+import common from './utils/common'; // 包含一些通用函数或常量
+import constant from './utils/constant'; // 包含一些配置常量
+
+import 'vfonts/FiraCode.css'; // 导入自定义字体样式
+import './assets/css/index.css'; // 导入全局样式文件
+import './assets/css/color.css'; // 导入颜色样式文件
+import './assets/css/animation.css'; // 导入动画样式文件
+
+// 使用naive-ui的create方法创建一个包含所有导入组件的插件实例
+const naive = create({
+ components: [
+ NAvatar, NInput, NIcon, NTag, NDivider, NButton,
+ NDrawer, NCard, NTabs, NTabPane, NSwitch, NModal, NBadge,
+ NPopover, NImage, NPopconfirm
+ ]
+});
+
+// 创建Vue应用实例
+const app = createApp(App);
+
+// 使用路由和Vuex状态管理
+app.use(router);
+app.use(store);
+
+// 使用naive-ui插件实例
+app.use(naive);
+
+// 单独注册element-plus的组件,以便在Vue应用中使用
+app.component(ElUpload.name, ElUpload);
+app.component(ElButton.name, ElButton);
+app.component(ElRadioGroup.name, ElRadioGroup);
+app.component(ElRadioButton.name, ElRadioButton);
+
+// 将一些全局属性挂载到Vue应用的配置对象上,以便在组件中通过this.$http等方式访问
+app.config.globalProperties.$http = http;
+app.config.globalProperties.$common = common;
+app.config.globalProperties.$constant = constant;
+
+// 路由守卫,用于在每个路由跳转前执行一些逻辑
+router.beforeEach((to, from, next) => {
+ // 如果目标路由需要认证
+ if (to.meta.requiresAuth) {
+ // 如果是首页且带有查询参数
+ if (to.path === "/") {
+ // 保存默认商店类型到localStorage
+ if (typeof to.query.defaultStoreType !== "undefined") {
+ localStorage.setItem("defaultStoreType", to.query.defaultStoreType);
+ }
+ // 如果带有userToken查询参数
+ if (typeof to.query.userToken !== "undefined") {
+ let userToken = to.query.userToken;
+ // 发送请求验证token
+ const xhr = new XMLHttpRequest();
+ xhr.open('post', constant.baseURL + "/user/token", false);
+ xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ xhr.send("userToken=" + userToken);
+ let result = JSON.parse(xhr.responseText);
+ // 如果验证成功,则加载用户信息并跳转到应用页面
+ if (!common.isEmpty(result) && result.code === 200) {
+ store.commit("loadCurrentUser", result.data);
+ localStorage.setItem("userToken", result.data.accessToken);
+ window.location.href = constant.imURL;
+ next();
+ } else {
+ // 验证失败或没有token,则跳转到网页版登录页面
+ window.location.href = constant.webBaseURL;
+ }
+ } else if (Boolean(localStorage.getItem("userToken"))) {
+ // 如果localStorage中有token,则直接放行
+ next();
+ } else {
+ // 如果没有token,则跳转到网页版登录页面
+ window.location.href = constant.webBaseURL;
+ }
+ } else {
+ // 如果不是首页,但路由需要认证
+ if (Boolean(localStorage.getItem("userToken"))) {
+ // 如果localStorage中有token,则直接放行
+ next();
+ } else {
+ // 如果没有token,则跳转到网页版登录页面
+ window.location.href = constant.webBaseURL;
+ }
+ }
+ } else {
+ // 如果目标路由不需要认证,则直接放行
+ next();
+ }
+});
+
+// 挂载Vue应用实例到#app元素上
+app.mount('#app');
diff --git a/注释(3) b/注释(3)
new file mode 100644
index 0000000..cf289ca
--- /dev/null
+++ b/注释(3)
@@ -0,0 +1,258 @@
+poetizepoetize-im-uisrcApp.vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+poetizepoetize-im-uisrcutilstiows.js
+
+// 导入reconnecting-websocket库,这是一个会自动重连的WebSocket实现
+import ReconnectingWebSocket from 'reconnecting-websocket';
+
+/**
+ * 创建一个WebSocket连接的构造函数
+ * @param {string} ws_protocol - WebSocket协议,'wss'用于加密连接,'ws'用于非加密连接
+ * @param {string} ip - WebSocket服务器的IP地址
+ * @param {string|number} port - WebSocket服务器的端口号,如果为空字符串则不使用端口号
+ * @param {string} paramStr - 附加在WebSocket URL后面的请求参数,例如:'name=张三&id=12'
+ * @param {string} binaryType - WebSocket接收二进制数据的类型,'blob'或'arraybuffer'
+ */
+export default function (ws_protocol, ip, port, paramStr, binaryType) {
+ // 将传入的参数赋值给当前对象的属性
+ this.ws_protocol = ws_protocol;
+ this.ip = ip;
+ this.port = port;
+ this.paramStr = paramStr;
+ this.binaryType = binaryType;
+
+ // 根据传入的参数构建WebSocket的URL
+ if (port === "") {
+ // 如果端口号为空字符串,则URL不包括端口号
+ this.url = ws_protocol + '://' + ip + '/socket';
+ } else {
+ // 否则,URL包括端口号
+ this.url = ws_protocol + '://' + ip + ":" + port + '/socket';
+ }
+ // 如果存在请求参数,则将它们添加到URL的末尾
+ if (paramStr) {
+ this.url += '?' + paramStr;
+ }
+
+ // 定义一个方法用于建立WebSocket连接
+ this.connect = () => {
+ // 创建一个新的ReconnectingWebSocket实例,并传入构建的URL
+ let ws = new ReconnectingWebSocket(this.url);
+ // 将创建的WebSocket实例保存到当前对象的ws属性中
+ this.ws = ws;
+ // 设置WebSocket接收二进制数据的类型
+ ws.binaryType = this.binaryType;
+
+ // 定义WebSocket连接打开时的回调函数
+ ws.onopen = function (event) {
+ // 在这里可以添加获取离线消息的逻辑,但当前为空实现
+ }
+
+ // 定义WebSocket连接关闭时的回调函数
+ ws.onclose = function (event) {
+ // 当前为空实现,可以在这里添加处理连接关闭的逻辑
+ }
+
+ // 定义WebSocket发生错误时的回调函数
+ ws.onerror = function (event) {
+ // 当前为空实现,可以在这里添加处理错误的逻辑
+ }
+ }
+
+ // 定义一个方法用于通过WebSocket发送数据
+ this.send = (data) => {
+ // 使用当前对象的ws属性(即WebSocket实例)的send方法发送数据
+ this.ws.send(data);
+ }
+}
+
+
+poetizepoetize-im-uisrcutilsrequest.js
+
+// 导入axios库,用于发送HTTP请求
+import axios from "axios";
+// 导入constant模块,该模块可能包含一些全局常量,如API的基础URL
+import constant from "./constant";
+// 导入qs库,用于处理URL参数序列化/反序列化
+import qs from "qs";
+
+// 导入Vuex的store实例,用于全局状态管理
+import store from "../store";
+
+// 设置axios的默认基础URL
+axios.defaults.baseURL = constant.baseURL;
+
+// 添加请求拦截器
+axios.interceptors.request.use(
+ function (config) {
+ // 在请求发送之前,可以在这里添加一些配置或处理逻辑
+ // 比如添加token到请求头中(但在这个例子中,token是在每个请求中单独添加的)
+ return config; // 返回配置对象,继续请求
+ },
+ function (error) {
+ // 请求发生错误时的处理逻辑
+ // 比如显示错误提示、记录日志等
+ return Promise.reject(error); // 返回Promise.reject,中断请求
+ }
+);
+
+// 添加响应拦截器
+axios.interceptors.response.use(
+ function (response) {
+ // 响应成功时的处理逻辑
+ // 检查响应数据中的code,如果不等于200,则视为错误处理
+ if (
+ response.data !== null &&
+ response.data.hasOwnProperty("code") &&
+ response.data.code !== 200
+ ) {
+ if (response.data.code === 300) {
+ // 特定错误码处理,比如用户未登录或token过期
+ store.commit("loadCurrentUser", {}); // 清除Vuex中的用户信息
+ localStorage.removeItem("userToken"); // 移除本地存储中的token
+ window.location.href = constant.webBaseURL + "/user"; // 重定向到登录页面
+ }
+ return Promise.reject(new Error(response.data.message)); // 返回Promise.reject,中断请求链,并携带错误信息
+ } else {
+ return response; // 返回响应对象,继续后续处理
+ }
+ },
+ function (error) {
+ // 响应发生错误时的处理逻辑
+ // 比如显示错误提示、记录日志等
+ return Promise.reject(error); // 返回Promise.reject,中断请求链
+ }
+);
+
+// 导出HTTP请求工具集
+export default {
+ // 发送POST请求的方法
+ post(url, params = {}, json = true) {
+ let config = {
+ headers: { "Authorization": localStorage.getItem("userToken") } // 添加token到请求头
+ };
+
+ return new Promise((resolve, reject) => {
+ axios
+ .post(
+ url,
+ json ? params : qs.stringify(params), // 根据json参数决定发送的数据格式
+ config
+ )
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ },
+
+ // 发送GET请求的方法
+ get(url, params = {}) {
+ let headers = { "Authorization": localStorage.getItem("userToken") }; // 添加token到请求头
+
+ return new Promise((resolve, reject) => {
+ axios.get(url, { params: params, headers: headers }) // 发送GET请求,携带参数和请求头
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ },
+
+ // 发送文件上传请求的方法
+ upload(url, param, option) {
+ let config = {
+ headers: {
+ "Authorization": localStorage.getItem("userToken"),
+ "Content-Type": "multipart/form-data" // 设置内容类型为文件上传
+ },
+ timeout: 60000 // 设置请求超时时间
+ };
+ if (typeof option !== "undefined") {
+ // 如果提供了上传进度回调,则添加onUploadProgress监听器
+ config.onUploadProgress = progressEvent => {
+ if (progressEvent.total > 0) {
+ progressEvent.percent = progressEvent.loaded / progressEvent.total * 100; // 计算上传进度百分比
+ }
+ option.onProgress(progressEvent); // 调用传入的回调,传递进度信息
+ };
+ }
+
+ return new Promise((resolve, reject) => {
+ axios
+ .post(url, param, config) // 发送POST请求,上传文件
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ },
+
+ // 发送七牛云文件上传请求的方法(可能不需要token,因为七牛云上传通常通过临时凭证)
+ uploadQiniu(url, param) {
+ let config = {
+ headers: { "Content-Type": "multipart/form-data" }, // 设置内容类型为文件上传
+ timeout: 60000 // 设置请求超时时间
+ };
+
+ return new Promise((resolve, reject) => {
+ axios
+ .post(url, param, config) // 发送POST请求,上传文件到七牛云
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ }
+};
diff --git a/注释(4) b/注释(4)
new file mode 100644
index 0000000..8428340
--- /dev/null
+++ b/注释(4)
@@ -0,0 +1,442 @@
+poetizepoetize-im-uisrcApp.vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+poetizepoetize-im-uisrcutilstiows.js
+
+// 导入reconnecting-websocket库,这是一个会自动重连的WebSocket实现
+import ReconnectingWebSocket from 'reconnecting-websocket';
+
+/**
+ * 创建一个WebSocket连接的构造函数
+ * @param {string} ws_protocol - WebSocket协议,'wss'用于加密连接,'ws'用于非加密连接
+ * @param {string} ip - WebSocket服务器的IP地址
+ * @param {string|number} port - WebSocket服务器的端口号,如果为空字符串则不使用端口号
+ * @param {string} paramStr - 附加在WebSocket URL后面的请求参数,例如:'name=张三&id=12'
+ * @param {string} binaryType - WebSocket接收二进制数据的类型,'blob'或'arraybuffer'
+ */
+export default function (ws_protocol, ip, port, paramStr, binaryType) {
+ // 将传入的参数赋值给当前对象的属性
+ this.ws_protocol = ws_protocol;
+ this.ip = ip;
+ this.port = port;
+ this.paramStr = paramStr;
+ this.binaryType = binaryType;
+
+ // 根据传入的参数构建WebSocket的URL
+ if (port === "") {
+ // 如果端口号为空字符串,则URL不包括端口号
+ this.url = ws_protocol + '://' + ip + '/socket';
+ } else {
+ // 否则,URL包括端口号
+ this.url = ws_protocol + '://' + ip + ":" + port + '/socket';
+ }
+ // 如果存在请求参数,则将它们添加到URL的末尾
+ if (paramStr) {
+ this.url += '?' + paramStr;
+ }
+
+ // 定义一个方法用于建立WebSocket连接
+ this.connect = () => {
+ // 创建一个新的ReconnectingWebSocket实例,并传入构建的URL
+ let ws = new ReconnectingWebSocket(this.url);
+ // 将创建的WebSocket实例保存到当前对象的ws属性中
+ this.ws = ws;
+ // 设置WebSocket接收二进制数据的类型
+ ws.binaryType = this.binaryType;
+
+ // 定义WebSocket连接打开时的回调函数
+ ws.onopen = function (event) {
+ // 在这里可以添加获取离线消息的逻辑,但当前为空实现
+ }
+
+ // 定义WebSocket连接关闭时的回调函数
+ ws.onclose = function (event) {
+ // 当前为空实现,可以在这里添加处理连接关闭的逻辑
+ }
+
+ // 定义WebSocket发生错误时的回调函数
+ ws.onerror = function (event) {
+ // 当前为空实现,可以在这里添加处理错误的逻辑
+ }
+ }
+
+ // 定义一个方法用于通过WebSocket发送数据
+ this.send = (data) => {
+ // 使用当前对象的ws属性(即WebSocket实例)的send方法发送数据
+ this.ws.send(data);
+ }
+}
+
+
+poetizepoetize-im-uisrcutilsrequest.js
+
+// 导入axios库,用于发送HTTP请求
+import axios from "axios";
+// 导入constant模块,该模块可能包含一些全局常量,如API的基础URL
+import constant from "./constant";
+// 导入qs库,用于处理URL参数序列化/反序列化
+import qs from "qs";
+
+// 导入Vuex的store实例,用于全局状态管理
+import store from "../store";
+
+// 设置axios的默认基础URL
+axios.defaults.baseURL = constant.baseURL;
+
+// 添加请求拦截器
+axios.interceptors.request.use(
+ function (config) {
+ // 在请求发送之前,可以在这里添加一些配置或处理逻辑
+ // 比如添加token到请求头中(但在这个例子中,token是在每个请求中单独添加的)
+ return config; // 返回配置对象,继续请求
+ },
+ function (error) {
+ // 请求发生错误时的处理逻辑
+ // 比如显示错误提示、记录日志等
+ return Promise.reject(error); // 返回Promise.reject,中断请求
+ }
+);
+
+// 添加响应拦截器
+axios.interceptors.response.use(
+ function (response) {
+ // 响应成功时的处理逻辑
+ // 检查响应数据中的code,如果不等于200,则视为错误处理
+ if (
+ response.data !== null &&
+ response.data.hasOwnProperty("code") &&
+ response.data.code !== 200
+ ) {
+ if (response.data.code === 300) {
+ // 特定错误码处理,比如用户未登录或token过期
+ store.commit("loadCurrentUser", {}); // 清除Vuex中的用户信息
+ localStorage.removeItem("userToken"); // 移除本地存储中的token
+ window.location.href = constant.webBaseURL + "/user"; // 重定向到登录页面
+ }
+ return Promise.reject(new Error(response.data.message)); // 返回Promise.reject,中断请求链,并携带错误信息
+ } else {
+ return response; // 返回响应对象,继续后续处理
+ }
+ },
+ function (error) {
+ // 响应发生错误时的处理逻辑
+ // 比如显示错误提示、记录日志等
+ return Promise.reject(error); // 返回Promise.reject,中断请求链
+ }
+);
+
+// 导出HTTP请求工具集
+export default {
+ // 发送POST请求的方法
+ post(url, params = {}, json = true) {
+ let config = {
+ headers: { "Authorization": localStorage.getItem("userToken") } // 添加token到请求头
+ };
+
+ return new Promise((resolve, reject) => {
+ axios
+ .post(
+ url,
+ json ? params : qs.stringify(params), // 根据json参数决定发送的数据格式
+ config
+ )
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ },
+
+ // 发送GET请求的方法
+ get(url, params = {}) {
+ let headers = { "Authorization": localStorage.getItem("userToken") }; // 添加token到请求头
+
+ return new Promise((resolve, reject) => {
+ axios.get(url, { params: params, headers: headers }) // 发送GET请求,携带参数和请求头
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ },
+
+ // 发送文件上传请求的方法
+ upload(url, param, option) {
+ let config = {
+ headers: {
+ "Authorization": localStorage.getItem("userToken"),
+ "Content-Type": "multipart/form-data" // 设置内容类型为文件上传
+ },
+ timeout: 60000 // 设置请求超时时间
+ };
+ if (typeof option !== "undefined") {
+ // 如果提供了上传进度回调,则添加onUploadProgress监听器
+ config.onUploadProgress = progressEvent => {
+ if (progressEvent.total > 0) {
+ progressEvent.percent = progressEvent.loaded / progressEvent.total * 100; // 计算上传进度百分比
+ }
+ option.onProgress(progressEvent); // 调用传入的回调,传递进度信息
+ };
+ }
+
+ return new Promise((resolve, reject) => {
+ axios
+ .post(url, param, config) // 发送POST请求,上传文件
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ },
+
+ // 发送七牛云文件上传请求的方法(可能不需要token,因为七牛云上传通常通过临时凭证)
+ uploadQiniu(url, param) {
+ let config = {
+ headers: { "Content-Type": "multipart/form-data" }, // 设置内容类型为文件上传
+ timeout: 60000 // 设置请求超时时间
+ };
+
+ return new Promise((resolve, reject) => {
+ axios
+ .post(url, param, config) // 发送POST请求,上传文件到七牛云
+ .then(res => {
+ resolve(res.data); // 请求成功,解析并返回响应数据
+ })
+ .catch(err => {
+ reject(err); // 请求失败,返回错误信息
+ });
+ });
+ }
+};
+
+
+poetizepoetize-im-uisrcutilsim.js
+
+// 引入常量配置文件
+import constant from "./constant";
+// 引入CryptoJS库,用于加密和解密操作
+import CryptoJS from 'crypto-js';
+// 引入Vuex的store实例,用于全局状态管理
+import store from '../store';
+// 引入Element Plus库的ElMessage组件,用于显示消息提示
+import {ElMessage} from "element-plus";
+
+// 导出一个包含多个工具函数的对象
+export default {
+ /**
+ * 判断当前设备是否为移动设备
+ * @returns {boolean} 如果是移动设备返回true,否则返回false
+ */
+ mobile() {
+ // 使用正则表达式匹配userAgent字符串,判断是否为移动设备
+ let flag = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
+ // 如果匹配到且匹配结果数组长度大于0,则返回true,表示是移动设备
+ return flag && flag.length > 0;
+ },
+
+ /**
+ * 判断一个值是否为空
+ * @param {any} value - 要判断的值
+ * @returns {boolean} 如果值为空返回true,否则返回false
+ */
+ isEmpty(value) {
+ // 判断值是否为undefined、null、空字符串、空数组或空对象
+ if (typeof value === "undefined" || value === null ||
+ (typeof value === "string" && value.trim() === "") ||
+ (Array.isArray(value) && value.length === 0) ||
+ (value.constructor === Object && Object.keys(value).length === 0)) {
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ /**
+ * 使用AES算法加密明文
+ * @param {string} plaintText - 要加密的明文
+ * @returns {string} 加密后的密文,经过Base64编码并替换特定字符
+ */
+ encrypt(plaintText) {
+ // 设置加密选项,使用ECB模式和Pkcs7填充
+ let options = {
+ mode: CryptoJS.mode.ECB,
+ padding: CryptoJS.pad.Pkcs7
+ };
+ // 从常量配置中获取加密密钥,并转换为Utf8编码
+ let key = CryptoJS.enc.Utf8.parse(constant.cryptojs_key);
+ // 使用AES算法加密明文,并转换为字符串,同时替换特定字符以适应某些场景
+ let encryptedData = CryptoJS.AES.encrypt(plaintText, key, options);
+ return encryptedData.toString().replace(/\//g, "_").replace(/\+/g, "-");
+ },
+
+ /**
+ * 使用AES算法解密密文
+ * @param {string} encryptedBase64Str - 要解密的密文,经过Base64编码并可能包含特定替换字符
+ * @returns {string} 解密后的明文
+ */
+ decrypt(encryptedBase64Str) {
+ // 将密文中的特定替换字符还原为Base64编码的字符
+ let val = encryptedBase64Str.replace(/\-/g, '+').replace(/_/g, '/');
+ // 设置解密选项,与加密时相同
+ let options = {
+ mode: CryptoJS.mode.ECB,
+ padding: CryptoJS.pad.Pkcs7
+ };
+ // 从常量配置中获取加密密钥,并转换为Utf8编码
+ let key = CryptoJS.enc.Utf8.parse(constant.cryptojs_key);
+ // 使用AES算法解密密文,并转换为Utf8编码的字符串
+ let decryptedData = CryptoJS.AES.decrypt(val, key, options);
+ return CryptoJS.enc.Utf8.stringify(decryptedData);
+ },
+
+ /**
+ * 将文本中的表情符号转换为对应的图片标签
+ * @param {string} content - 包含表情符号的文本
+ * @returns {string} 表情符号被替换为图片标签后的文本
+ */
+ faceReg(content) {
+ // 使用正则表达式匹配文本中的表情符号,并替换为对应的图片标签
+ content = content.replace(/
+$$
+[^\[^
+$$
+]+\]/g, (word) => {
+ // 从常量配置的表情符号列表中查找匹配的索引
+ let index = constant.emojiList.indexOf(word.replace("[", "").replace("]", ""));
+ if (index > -1) {
+ // 根据索引构造图片URL,并返回图片标签
+ let url = store.state.sysConfig['webStaticResourcePrefix'] + "emoji/q" + (index + 1) + ".gif";
+ return ' ';
+ } else {
+ // 如果没有找到匹配的表情符号,则原样返回
+ return word;
+ }
+ });
+ return content;
+ },
+
+ /**
+ * 将文本中的图片链接转换为图片标签
+ * @param {string} content - 包含图片链接的文本
+ * @returns {string} 图片链接被替换为图片标签后的文本
+ */
+ pictureReg(content) {
+ // 使用正则表达式匹配文本中的图片链接,并替换为对应的图片标签
+ content = content.replace(/
+$$
+[^\[^
+$$
+]+\]/g, (word) => {
+ // 查找图片链接中的逗号分隔符,以获取图片描述和链接
+ let index = word.indexOf(",");
+ if (index > -1) {
+ let arr = word.replace("[", "").replace("]", "").split(",");
+ // 返回图片标签,包含描述作为title属性
+ return ' ';
+ } else {
+ // 如果没有找到逗号分隔符,则原样返回
+ return word;
+ }
+ });
+ return content;
+ },
+
+ /**
+ * 将日期字符串转换为时间戳
+ * @param {string} dateStr - 日期字符串,格式为YYYY-MM-DD或YYYY-MM-DD HH:mm:ss
+ * @returns {number} 转换后的时间戳(毫秒)
+ */
+ getDateTimeStamp(dateStr) {
+ // 将日期字符串中的短横线替换为斜杠,以适应Date.parse方法的格式要求
+ return Date.parse(dateStr.replace(/-/gi, "/"));
+ },
+
+ /**
+ * 计算两个日期之间的时间差,并返回友好的时间差字符串
+ * @param {string} dateStr - 要比较的日期字符串,格式为YYYY-MM-DD HH:mm:ss
+ * @returns {string} 友好的时间差字符串,如“3天前”、“2小时前”等
+ */
+ getDateDiff(dateStr) {
+ // 将日期字符串转换为时间戳(秒),并获取当前时间的时间戳(秒)
+ let publishTime = Date.parse(dateStr.replace(/-/gi, "/")) / 1000,
+ timeNow = Math.floor(new Date().getTime() / 1000),
+ d = timeNow - publishTime, // 计算时间差(秒)
+ // ...(省略了部分代码,包括日期格式化和时间差计算的详细逻辑)
+ // 根据时间差返回不同的友好时间差字符串
+ if (d_days > 0 && d_days < 3) {
+ return d_days + '天前';
+ } else if (d_days <= 0 && d_hours > 0) {
+ return d_hours + '小时前';
+ } else if (d_hours <= 0 && d_minutes > 0) {
+ return d_minutes + '分钟前';
+ } else if (d_seconds < 60) {
+ if (d_seconds <= 0) {
+ return '刚刚发表';
+ } else {
+ return d_seconds + '秒前';
+ }
+ } else if (d_days >= 3 && d_days < 30) {
+ return M + '-' + D + ' ' + H + ':' + m;
+ } else if (d_days >= 30) {
+ return Y + '-' + M + '-' + D + ' ' + H + ':' + m;
+ }
+ },
+
+ /**
+ * 保存资源信息到服务器
+ * @param {Vue组件实例} that - 调用此方法的Vue组件实例
+ * @param {string} type - 资源类型
+ * @param {string} path - 资源路径
+ * @param {number} size - 资源大小(字节)
+ * @param {string} mimeType - 资源MIME类型
+ * @param {string} originalName - 资源原始名称
+ * @param {string} storeType - 存储类型
+ */
diff --git a/注释(5) b/注释(5)
new file mode 100644
index 0000000..892a781
--- /dev/null
+++ b/注释(5)
@@ -0,0 +1,634 @@
+poetizepoetize-im-uisrcstoreindex.js
+
+// 从'vuex'包中导入createStore函数,用于创建一个新的Vuex store实例
+import { createStore } from 'vuex';
+
+// 导出通过createStore函数创建的Vuex store实例
+export default createStore({
+ // state对象包含了应用的状态
+ // 这些状态可以在应用的任何组件中通过this.$store.state访问
+ state: {
+ // 从localStorage中获取当前用户信息,如果不存在则默认为空对象{}
+ // JSON.parse用于将JSON字符串转换为JavaScript对象
+ currentUser: JSON.parse(localStorage.getItem("currentUser") || '{}'),
+ // 从localStorage中获取系统配置信息,如果不存在则默认为空对象{}
+ sysConfig: JSON.parse(localStorage.getItem("sysConfig") || '{}')
+ },
+
+ // getters对象可以包含一些方法,这些方法基于state中的状态计算并返回一些值
+ // 在这个例子中,getters对象是空的,意味着没有定义任何getter
+ getters: {},
+
+ // mutations对象包含了直接修改状态的方法(同步函数)
+ // Vuex要求更改Vuex的store中的状态的唯一方法是提交mutation
+ mutations: {
+ // loadCurrentUser方法用于加载当前用户信息
+ // 它接受两个参数:state(当前Vuex store的状态对象)和user(要设置的用户信息对象)
+ // 这个方法会将用户信息保存到state中,并更新localStorage中的currentUser项
+ loadCurrentUser(state, user) {
+ state.currentUser = user;
+ localStorage.setItem("currentUser", JSON.stringify(user));
+ },
+ // loadSysConfig方法用于加载系统配置信息
+ // 它接受两个参数:state(当前Vuex store的状态对象)和sysConfig(要设置的系统配置信息对象)
+ // 这个方法会将系统配置信息保存到state中,并更新localStorage中的sysConfig项
+ loadSysConfig(state, sysConfig) {
+ state.sysConfig = sysConfig;
+ localStorage.setItem("sysConfig", JSON.stringify(sysConfig));
+ }
+ },
+
+ // actions对象包含了可以包含任意异步操作的函数
+ // 它们通过提交mutations而不是直接更改状态来更改Vuex store中的状态
+ // 在这个例子中,actions对象是空的,意味着没有定义任何action
+ actions: {},
+
+ // modules对象允许将store分割成模块(module),每个模块拥有自己的state、mutation、action、getter
+ // 在这个例子中,modules对象是空的,意味着没有定义任何模块
+ modules: {},
+
+ // plugins数组允许你注册Vuex插件,插件可以扩展Vuex的功能
+ // 在这个例子中,plugins数组是空的,意味着没有注册任何插件
+ plugins: []
+});
+
+
+poetizepoetize-im-uisrcrouterindex.js
+
+// 从'vue-router'包中导入createRouter和createWebHistory函数
+// createRouter用于创建一个新的router实例
+// createWebHistory用于创建一个基于HTML5历史记录API的history实例
+import { createRouter, createWebHistory } from 'vue-router';
+
+// 从相对路径"../utils/constant"中导入constant模块,该模块可能包含一些常量定义
+import constant from "../utils/constant";
+
+// 定义一个路由配置数组,每个对象代表一个路由规则
+const routes = [
+ {
+ // 路由的路径,当用户访问"/"时,此路由将被匹配
+ path: "/",
+
+ // 路由的meta字段,用于存储一些自定义信息,如权限要求等
+ // 在这个例子中,定义了一个requiresAuth: true,表示访问此路由需要用户认证
+ meta: { requiresAuth: true },
+
+ // 路由的组件,这里使用了一个懒加载的组件
+ // () => import('../components/index') 是一个异步组件加载的语法,表示当用户访问此路由时,才加载并渲染'index'组件
+ component: () => import('../components/index')
+ }
+ // 注意:这里只定义了一个路由规则,实际项目中可能会有更多
+];
+
+// 创建一个新的router实例,并传入配置对象
+const router = createRouter({
+ // 使用createWebHistory函数创建一个history实例,并传入constant.webHistory作为配置项
+ // constant.webHistory可能是一个配置项,用于定义history的行为,如基础路径等
+ // 但具体值取决于constant模块的实现,这里无法直接知道
+ history: createWebHistory(constant.webHistory),
+
+ // 传入之前定义的路由规则数组
+ routes,
+
+ // 定义一个scrollBehavior函数,用于控制路由跳转后的滚动行为
+ // 当用户从一个路由跳转到另一个路由时,这个函数会被调用
+ // to和from是路由对象,分别表示目标路由和来源路由
+ // savedPosition是一个对象,如果页面之前被滚动过,这里会包含滚动位置的信息
+ // 函数返回一个对象,指定滚动到页面的哪个位置
+ // 在这个例子中,总是滚动到页面顶部(left: 0, top: 0)
+ scrollBehavior(to, from, savedPosition) {
+ return { left: 0, top: 0 };
+ }
+});
+
+// 导出router实例,使其可以在Vue应用的创建过程中被使用
+export default router;
+
+
+poetizepoetize-im-uisrchooksbindEmail.js
+
+// 导入Vuex的useStore钩子,用于访问Vuex store
+import { useStore } from 'vuex';
+
+// 导入Naive UI的useDialog钩子,用于显示对话框
+import { useDialog } from 'naive-ui';
+
+// 导入Vue的nextTick函数,用于在下次DOM更新循环结束之后执行延迟回调
+import { nextTick } from 'vue';
+
+// 导入Element Plus的ElMessage组件,用于显示消息提示
+import { ElMessage } from "element-plus";
+
+// 导入Vue的reactive、getCurrentInstance、onMounted、onBeforeUnmount、watchEffect、toRefs函数
+import { reactive, getCurrentInstance, onMounted, onBeforeUnmount, watchEffect, toRefs } from 'vue';
+
+// 定义一个函数,该函数返回一个对象,该对象包含响应式数据和方法
+export default function () {
+ // 获取当前Vue实例的上下文,并从中提取全局属性
+ const globalProperties = getCurrentInstance().appContext.config.globalProperties;
+ const $common = globalProperties.$common; // 自定义的常用方法集合
+ const $http = globalProperties.$http; // 自定义的HTTP请求方法
+ const $constant = globalProperties.$constant; // 自定义的常量集合
+
+ // 使用Vuex的useStore钩子获取store实例
+ const store = useStore();
+
+ // 使用Naive UI的useDialog钩子获取对话框实例
+ const dialog = useDialog();
+
+ // 定义响应式数据对象,用于绑定邮箱相关的数据
+ let bindEmailData = reactive({
+ emailVisible: false, // 邮箱对话框的显示状态
+ email: '', // 邮箱地址
+ code: '', // 验证码
+ password: '', // 密码
+ codeString: "验证码" // 倒计时显示的字符串,初始为"验证码"
+ });
+
+ // 定义一个变量,用于存储验证码倒计时的setInterval返回值
+ let intervalCode = null;
+
+ // 在组件挂载时执行的方法
+ onMounted(() => {
+ showEmail(); // 调用showEmail方法检查是否需要显示邮箱绑定对话框
+ });
+
+ // 显示邮箱绑定对话框的逻辑(但当前代码未直接设置对话框可见性)
+ function showEmail() {
+ // 检查当前用户是否存在且未绑定邮箱,则可能需要显示对话框(但当前逻辑未实现)
+ if (!$common.isEmpty(store.state.currentUser) && $common.isEmpty(store.state.currentUser.email)) {
+ // 逻辑未实现,仅注释说明需要显示对话框
+ // bindEmailData.emailVisible = true;
+ }
+ }
+
+ // 获取验证码的逻辑
+ function getCode() {
+ // 检查验证码倒计时是否正在进行,如果正在进行则提示稍后再试
+ if (bindEmailData.codeString === "验证码") {
+ // 校验邮箱格式并发送验证码请求
+ // ...(省略了具体的校验和请求逻辑)
+
+ // 设置验证码倒计时
+ bindEmailData.codeString = "30";
+ intervalCode = setInterval(() => {
+ // 每秒更新一次倒计时,直到倒计时结束
+ // ...(省略了具体的倒计时逻辑)
+ }, 1000);
+ } else {
+ // 提示稍后再试
+ ElMessage({
+ message: "请稍后再试!",
+ type: 'error'
+ });
+ }
+ }
+
+ // 提交邮箱绑定对话框的逻辑
+ function submitDialog() {
+ // 校验邮箱、验证码和密码(省略了具体的校验逻辑)
+ // ...
+
+ // 发送绑定邮箱的请求
+ // ...(省略了具体的请求逻辑)
+ }
+
+ // 清除邮箱绑定对话框的数据和状态
+ function clearEmailDialog() {
+ // 重置对话框数据和状态
+ // ...(省略了具体的重置逻辑)
+ }
+
+ // 返回响应式数据和方法,以便在组件中使用
+ return {
+ bindEmailData,
+ getCode,
+ submitDialog
+ };
+}
+
+
+poetizepoetize-im-uisrchookschangeData.js
+
+// 导入所需的Vue Composition API函数、Vuex store、Naive UI对话框组件和Element Plus消息组件
+import { useStore } from 'vuex';
+import { useDialog } from 'naive-ui';
+import { nextTick } from 'vue'; // 导入nextTick但未在代码中使用
+import { ElMessage } from "element-plus";
+import {
+ reactive,
+ getCurrentInstance,
+ onMounted,
+ onBeforeUnmount,
+ watchEffect,
+ toRefs
+} from 'vue';
+
+// 定义一个函数,接收friendData和groupData作为参数
+export default function (friendData, groupData) {
+ // 获取Vue实例的全局属性
+ const globalProperties = getCurrentInstance().appContext.config.globalProperties;
+ const $common = globalProperties.$common; // 通用方法
+ const $http = globalProperties.$http; // HTTP请求方法
+ const $constant = globalProperties.$constant; // 常量配置
+ const store = useStore(); // 获取Vuex store实例
+ const dialog = useDialog(); // 获取Naive UI对话框实例
+
+ // 使用reactive创建一个响应式对象,用于存储修改信息
+ let changeDataData = reactive({
+ changeData: '', // 修改后的数据
+ changeType: null, // 修改类型
+ changeModal: false, // 修改信息模态框显示状态
+ avatarType: null, // 头像类型(用户或群组)
+ avatarPrefix: '', // 头像URL前缀
+ showAvatarDialog: false // 显示头像修改对话框
+ });
+
+ // 关闭模态框的函数,重置相关状态
+ function closeModal() {
+ changeDataData.avatarType = null;
+ changeDataData.avatarPrefix = '';
+ changeDataData.changeData = '';
+ changeDataData.changeType = null;
+ }
+
+ // 显示头像修改对话框的函数,根据类型设置前缀
+ function changeAvatar(type) {
+ // 判断是否有权限修改头像
+ if (type === 1 || (type === 2 && groupData.groups[groupData.currentGroupId].masterFlag)) {
+ closeModal(); // 关闭其他模态框
+ changeDataData.showAvatarDialog = true; // 显示头像修改对话框
+ changeDataData.avatarType = type; // 设置头像类型
+ if (type === 1) {
+ changeDataData.avatarPrefix = 'userAvatar'; // 用户头像前缀
+ } else if (type === 2) {
+ changeDataData.avatarPrefix = 'im/groupAvatar'; // 群组头像前缀
+ }
+ }
+ }
+
+ // 改变数据类型(修改备注、群名等)的函数
+ function changeDataType(type) {
+ closeModal(); // 关闭其他模态框
+ changeDataData.changeType = type; // 设置修改类型
+ changeDataData.changeModal = true; // 显示修改信息模态框
+ }
+
+ // 提交头像修改的函数
+ function submitAvatar(avatar) {
+ if ($common.isEmpty(avatar)) {
+ ElMessage({ message: "请上传头像!", type: 'warning' }); // 提示上传头像
+ return;
+ }
+ // 根据头像类型执行相应的修改操作
+ if (changeDataData.avatarType === 1) {
+ // 用户头像修改逻辑
+ } else if (changeDataData.avatarType === 2) {
+ // 群组头像修改逻辑
+ }
+ }
+
+ // 提交修改的函数(备注、群名、公告、简介)
+ function submitChange() {
+ // 根据修改类型执行相应的修改操作
+ if (changeDataData.changeType === 1) {
+ // 修改备注逻辑
+ } else if (changeDataData.changeType === 2) {
+ // 修改群名逻辑
+ } else if (changeDataData.changeType === 3) {
+ // 修改公告逻辑
+ } else if (changeDataData.changeType === 4) {
+ // 修改简介逻辑
+ }
+ }
+
+ // 返回对象,包含状态和方法供外部使用
+ return {
+ changeDataData,
+ changeAvatar,
+ changeDataType,
+ submitAvatar,
+ submitChange
+ };
+}
+
+
+poetizepoetize-im-uisrchooksfriend.js
+
+// 引入Vuex的useStore钩子,用于访问Vuex store
+import {useStore} from 'vuex';
+
+// 引入Naive UI的useDialog钩子,用于显示对话框
+import {useDialog} from 'naive-ui';
+
+// 引入Vue的nextTick函数,用于在下次DOM更新循环结束之后执行延迟回调
+import {nextTick} from 'vue';
+
+// 引入Element Plus的ElMessage组件,用于显示消息提示
+import {ElMessage} from "element-plus";
+
+// 引入Vue的reactive、getCurrentInstance、onMounted、onBeforeUnmount、watchEffect、toRefs函数
+import {reactive, getCurrentInstance, onMounted, onBeforeUnmount, watchEffect, toRefs} from 'vue';
+
+export default function () {
+ // 获取当前Vue实例的上下文,并从中获取全局属性
+ const globalProperties = getCurrentInstance().appContext.config.globalProperties;
+ const $common = globalProperties.$common; // 通用方法或属性
+ const $http = globalProperties.$http; // HTTP请求方法
+ const $constant = globalProperties.$constant; // 常量配置
+
+ // 使用Vuex的useStore钩子获取store实例
+ const store = useStore();
+
+ // 使用Naive UI的useDialog钩子获取对话框实例
+ const dialog = useDialog();
+
+ // 使用reactive创建一个响应式对象,用于存储好友数据
+ let friendData = reactive({
+ // 好友请求列表
+ friendRequests: [],
+ // 好友列表,使用friendId作为键
+ friends: {},
+ // 当前操作的好友ID
+ currentFriendId: null
+ });
+
+ // 异步函数,用于获取当前用户的好友列表
+ async function getImFriend() {
+ try {
+ const res = await $http.get($constant.baseURL + "/imChatUserFriend/getFriend", {friendStatus: 1});
+ if (!$common.isEmpty(res.data)) {
+ res.data.forEach(friend => {
+ friendData.friends[friend.friendId] = friend;
+ });
+ }
+ } catch (error) {
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ }
+ }
+
+ // 删除好友的函数
+ function removeFriend(currentFriendId) {
+ dialog.error({
+ title: '警告',
+ content: `你确定删除${friendData.friends[currentFriendId].remark}?`,
+ positiveText: '确定',
+ onPositiveClick: async () => {
+ try {
+ const res = await $http.get($constant.baseURL + "/imChatUserFriend/changeFriend", {
+ friendId: currentFriendId,
+ friendStatus: -1
+ });
+ delete friendData.friends[currentFriendId];
+ friendData.currentFriendId = null;
+ ElMessage({
+ message: "删除成功!",
+ type: 'success'
+ });
+ } catch (error) {
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ }
+ }
+ });
+ }
+
+ // 获取好友请求的函数
+ function getFriendRequests() {
+ $http.get($constant.baseURL + "/imChatUserFriend/getFriend", {friendStatus: 0})
+ .then((res) => {
+ if (!$common.isEmpty(res.data)) {
+ friendData.friendRequests = res.data;
+ ElMessage({
+ message: "您有好友申请待处理!",
+ showClose: true,
+ type: 'success',
+ duration: 0
+ });
+ }
+ })
+ .catch((error) => {
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 修改好友请求状态的函数
+ function changeFriendStatus(friendId, status, index) {
+ $http.get($constant.baseURL + "/imChatUserFriend/changeFriend", {friendId: friendId, friendStatus: status})
+ .then((res) => {
+ friendData.friendRequests.splice(index, 1);
+ ElMessage({
+ message: "修改成功!",
+ type: 'success'
+ });
+ })
+ .catch((error) => {
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 返回对象,包含响应式数据和函数,供外部使用
+ return {
+ friendData,
+ getImFriend,
+ removeFriend,
+ getFriendRequests,
+ changeFriendStatus
+ };
+}
+
+
+poetizepoetize-im-uisrchooksfriendCircle.js
+
+import {useStore} from 'vuex'; // 引入Vuex的useStore函数,用于访问Vuex store
+import {useDialog} from 'naive-ui'; // 引入Naive UI的useDialog函数,用于弹出对话框
+import {nextTick} from 'vue'; // 引入Vue的nextTick函数,用于在下次DOM更新循环结束之后执行延迟回调
+import {ElMessage} from "element-plus"; // 引入Element Plus的ElMessage组件,用于显示消息提示
+import {reactive, getCurrentInstance, onMounted, onBeforeUnmount, watchEffect, toRefs} from 'vue'; // 引入Vue的响应式API和其他生命周期钩子
+
+export default function () {
+ // 获取Vue实例的全局属性
+ const globalProperties = getCurrentInstance().appContext.config.globalProperties;
+ const $common = globalProperties.$common; // 获取全局的common工具集
+ const $http = globalProperties.$http; // 获取全局的http请求方法
+ const $constant = globalProperties.$constant; // 获取全局的常量配置
+ const store = useStore(); // 使用Vuex store
+ const dialog = useDialog(); // 使用Naive UI的对话框
+
+ // 定义响应式数据
+ let friendCircleData = reactive({
+ showFriendCircle: false, // 是否显示朋友圈
+ treeHoleList: [], // 朋友圈列表数据
+ weiYanDialogVisible: false, // 是否显示发表朋友圈对话框
+ isPublic: true, // 发表的朋友圈是否公开
+ weiYanAvatar: '', // 发表朋友圈的用户头像
+ weiYanUsername: '', // 发表朋友圈的用户名
+ pagination: { // 分页配置
+ current: 1,
+ size: 10,
+ total: 0,
+ userId: null
+ }
+ });
+
+ // 显示发表朋友圈对话框
+ function launch() {
+ friendCircleData.weiYanDialogVisible = true;
+ }
+
+ // 打开指定用户的朋友圈
+ function openFriendCircle(userId, avatar, username) {
+ friendCircleData.pagination.userId = userId; // 设置当前用户ID
+ friendCircleData.weiYanAvatar = avatar; // 设置用户头像
+ friendCircleData.weiYanUsername = username; // 设置用户名
+ getWeiYan(); // 获取朋友圈数据
+ }
+
+ // 删除朋友圈动态
+ function deleteTreeHole(id) {
+ dialog.error({ // 弹出确认删除对话框
+ title: '警告',
+ content: '确定删除?',
+ positiveText: '确定',
+ onPositiveClick: () => {
+ $http.get($constant.baseURL + "/weiYan/deleteWeiYan", {id: id}) // 发送删除请求
+ .then((res) => {
+ ElMessage({ // 提示删除成功
+ message: "删除成功!",
+ type: 'success'
+ });
+ resetPaginationAndList(); // 重置分页和列表数据
+ getWeiYan(); // 重新获取朋友圈数据
+ })
+ .catch((error) => {
+ ElMessage({ // 提示错误信息
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+ });
+ }
+
+ // 获取朋友圈数据
+ function getWeiYan() {
+ $http.post($constant.baseURL + "/weiYan/listWeiYan", friendCircleData.pagination)
+ .then((res) => {
+ if (!$common.isEmpty(res.data)) { // 如果返回的数据不为空
+ processRecords(res.data.records); // 处理朋友圈记录
+ friendCircleData.treeHoleList = friendCircleData.treeHoleList.concat(res.data.records); // 合并到列表
+ friendCircleData.pagination.total = res.data.total; // 更新总记录数
+ friendCircleData.showFriendCircle = true; // 显示朋友圈
+ }
+ })
+ .catch((error) => {
+ ElMessage({ // 提示错误信息
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 提交发表朋友圈
+ function submitWeiYan(content) {
+ let weiYan = {
+ content: content,
+ isPublic: friendCircleData.isPublic
+ };
+
+ $http.post($constant.baseURL + "/weiYan/saveWeiYan", weiYan)
+ .then((res) => {
+ resetPaginationAndList(); // 重置分页和列表数据
+ friendCircleData.weiYanDialogVisible = false; // 关闭发表对话框
+ getWeiYan(); // 重新获取朋友圈数据
+ })
+ .catch((error) => {
+ ElMessage({ // 提示错误信息
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 重置分页和列表数据
+ function resetPaginationAndList() {
+ friendCircleData.pagination = {
+ current: 1,
+ size: 10,
+ total: 0,
+ userId: null
+ };
+ friendCircleData.treeHoleList = [];
+ }
+
+ // 清理朋友圈数据
+ function cleanFriendCircle() {
+ resetPaginationAndList(); // 重置分页和列表数据
+ friendCircleData.weiYanAvatar = ''; // 清空头像
+ friendCircleData.weiYanUsername = ''; // 清空用户名
+ friendCircleData.showFriendCircle = false; // 隐藏朋友圈
+ }
+
+ // 下一页朋友圈数据
+ function pageWeiYan() {
+ friendCircleData.pagination.current = friendCircleData.pagination.current + 1; // 更新当前页码
+ getWeiYan(); // 重新获取朋友圈数据
+ }
+
+ // 添加好友
+ function addFriend() {
+ dialog.success({ // 弹出确认添加好友对话框
+ title: '好友申请',
+ content: '确认提交好友申请,添加 ' + friendCircleData.weiYanUsername + ' 为好友?',
+ positiveText: '确定',
+ onPositiveClick: () => {
+ $http.get($constant.baseURL + "/imChatUserFriend/addFriend", {friendId: friendCircleData.pagination.userId})
+ .then((res) => {
+ ElMessage({ // 提示提交成功
+ message: "提交成功!",
+ type: 'success'
+ });
+ })
+ .catch((error) => {
+ ElMessage({ // 提示错误信息
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+ });
+ }
+
+ // 处理朋友圈记录,包括替换换行符和表情符号等
+ function processRecords(records) {
+ records.forEach(c => {
+ c.content = c.content.replace(/\n{2,}/g, '
'); // 替换连续换行符
+ c.content = c.content.replace(/\n/g, ' '); // 替换单个换行符
+ c.content = $common.faceReg(c.content); // 替换表情符号
+ c.content = $common.pictureReg(c.content); // 替换图片链接
+ });
+ }
+
+ // 返回对外暴露的方法和数据
+ return {
+ friendCircleData,
+ launch,
+ openFriendCircle,
+ deleteTreeHole,
+ submitWeiYan,
+ pageWeiYan,
+ cleanFriendCircle,
+ addFriend
+ };
+}
diff --git a/注释(6) b/注释(6)
new file mode 100644
index 0000000..ec26fb2
--- /dev/null
+++ b/注释(6)
@@ -0,0 +1,469 @@
+poetizepoetize-im-uisrchooksgroup.js
+
+// 引入必要的Vuex、Naive UI、Vue和Element Plus的函数和对象
+import { useStore } from 'vuex'; // Vuex的useStore钩子,用于访问Vuex store
+import { useDialog } from 'naive-ui'; // Naive UI的useDialog钩子,用于显示对话框
+import { nextTick } from 'vue'; // Vue的nextTick函数,用于延迟执行到下次DOM更新循环之后
+import { ElMessage } from "element-plus"; // Element Plus的ElMessage,用于显示消息提示
+import {
+ reactive, // Vue的reactive函数,用于创建一个响应式对象
+ getCurrentInstance, // Vue的getCurrentInstance函数,用于获取当前组件实例
+ onMounted, // Vue的onMounted生命周期钩子,用于组件挂载后执行
+ onBeforeUnmount, // Vue的onBeforeUnmount生命周期钩子,用于组件卸载前执行
+ watchEffect, // Vue的watchEffect函数,用于执行副作用
+ toRefs // Vue的toRefs函数,用于将响应式对象转换为响应式引用对象
+} from 'vue';
+
+export default function () {
+ // 获取当前Vue应用的全局属性
+ const globalProperties = getCurrentInstance().appContext.config.globalProperties;
+ const $common = globalProperties.$common; // 自定义的全局方法或属性集合
+ const $http = globalProperties.$http; // 自定义的HTTP请求方法
+ const $constant = globalProperties.$constant; // 自定义的常量集合
+ const store = useStore(); // 访问Vuex store
+ const dialog = useDialog(); // 访问Naive UI的对话框
+
+ // 定义响应式对象,用于存储群组数据和当前群组ID
+ let groupData = reactive({
+ groups: {}, // 群组列表,键为群组ID,值为群组信息
+ currentGroupId: null // 当前选中的群组ID
+ });
+
+ // 退出群组的功能
+ function exitGroup(currentGroupId) {
+ $http.get($constant.baseURL + "/imChatGroupUser/quitGroup", {id: currentGroupId})
+ .then((res) => {
+ // 成功退出群组后,从群组列表中删除当前群组,并重置当前群组ID
+ delete groupData.groups[currentGroupId];
+ groupData.currentGroupId = null;
+ // 显示成功消息
+ ElMessage({
+ message: "退群成功!",
+ type: 'success'
+ });
+ })
+ .catch((error) => {
+ // 请求失败时显示错误消息
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 解散群组的功能
+ function dissolveGroup(currentGroupId) {
+ $http.get($constant.baseURL + "/imChatGroup/deleteGroup", {id: currentGroupId})
+ .then((res) => {
+ // 成功解散群组后,从群组列表中删除当前群组,并重置当前群组ID
+ delete groupData.groups[currentGroupId];
+ groupData.currentGroupId = null;
+ // 显示成功消息
+ ElMessage({
+ message: "解散群成功!",
+ type: 'success'
+ });
+ })
+ .catch((error) => {
+ // 请求失败时显示错误消息
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 获取群组列表的功能
+ async function getImGroup() {
+ await $http.get($constant.baseURL + "/imChatGroup/listGroup")
+ .then((res) => {
+ // 如果返回的数据不为空,则遍历数据并更新群组列表
+ if (!$common.isEmpty(res.data)) {
+ res.data.forEach(group => {
+ groupData.groups[group.id] = group;
+ });
+ }
+ })
+ .catch((error) => {
+ // 请求失败时显示错误消息
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 添加群组话题的功能(此函数当前为空,可能需要在后续实现)
+ function addGroupTopic() {
+ $http.get($constant.baseURL + "/imChatGroup/addGroupTopic", {id: groupData.currentGroupId})
+ .then((res) => {
+ // 成功后的处理(当前为空)
+ })
+ .catch((error) => {
+ // 请求失败时显示错误消息
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 返回对外暴露的属性和方法
+ return {
+ groupData, // 群组数据和当前群组ID
+ getImGroup, // 获取群组列表的方法
+ addGroupTopic, // 添加群组话题的方法(当前为空)
+ exitGroup, // 退出群组的方法
+ dissolveGroup // 解散群组的方法
+ };
+}
+
+
+poetizepoetize-im-uisrchooksimUtil.js
+
+import {useStore} from 'vuex'; // 从vuex导入useStore,用于访问Vuex状态管理
+import {useDialog} from 'naive-ui'; // 从naive-ui导入useDialog,用于控制对话框
+import {nextTick} from 'vue'; // 导入nextTick,用于在下次DOM更新循环结束之后执行延迟回调
+import {ElMessage} from "element-plus"; // 从element-plus导入ElMessage,用于显示消息提示
+import {reactive, getCurrentInstance, onMounted, onBeforeUnmount, watchEffect, toRefs} from 'vue'; // 从vue导入多个API,用于响应式数据、获取当前实例、生命周期钩子、观察副作用等
+
+export default function () {
+ // 获取Vue应用的全局属性
+ const globalProperties = getCurrentInstance().appContext.config.globalProperties;
+ const $common = globalProperties.$common; // 获取全局的通用方法
+ const $http = globalProperties.$http; // 获取全局的HTTP请求方法
+ const $constant = globalProperties.$constant; // 获取全局的常量配置
+ const store = useStore(); // 使用Vuex的store
+ const dialog = useDialog(); // 使用naive-ui的对话框
+
+ // 定义响应式数据
+ let imUtilData = reactive({
+ systemMessages: [], // 系统消息列表
+ showBodyLeft: true, // 控制左侧区域显示状态的标志
+ imageList: [] // 表情包图片列表
+ });
+
+ // 组件挂载后执行的逻辑
+ onMounted(() => {
+ if ($common.mobile()) { // 如果是移动设备
+ $(".friend-aside").click(function () { // 点击左侧区域
+ imUtilData.showBodyLeft = true; // 显示左侧区域
+ mobileRight(); // 调整右侧区域的样式
+ });
+
+ $(".body-right").click(function () { // 点击右侧区域
+ imUtilData.showBodyLeft = false; // 隐藏左侧区域
+ mobileRight(); // 调整右侧区域的样式
+ });
+ }
+ mobileRight(); // 初始化右侧区域的样式
+ });
+
+ // 切换左侧区域的显示状态
+ function changeAside() {
+ imUtilData.showBodyLeft = !imUtilData.showBodyLeft;
+ mobileRight();
+ }
+
+ // 根据showBodyLeft的值调整右侧区域的样式
+ function mobileRight() {
+ if (imUtilData.showBodyLeft && $common.mobile()) {
+ $(".body-right").addClass("mobile-right"); // 添加样式以显示右侧区域
+ } else if (!imUtilData.showBodyLeft && $common.mobile()) {
+ $(".body-right").removeClass("mobile-right"); // 移除样式以隐藏右侧区域
+ }
+ }
+
+ // 获取系统消息
+ function getSystemMessages() {
+ $http.get($constant.baseURL + "/imChatUserMessage/listSystemMessage")
+ .then((res) => {
+ if (!$common.isEmpty(res.data) && !$common.isEmpty(res.data.records)) {
+ imUtilData.systemMessages = res.data.records; // 更新系统消息列表
+ }
+ })
+ .catch((error) => {
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 隐藏左侧区域(重复代码,建议合并或移除)
+ function hiddenBodyLeft() {
+ if ($common.mobile()) {
+ $(".body-right").click(function () {
+ imUtilData.showBodyLeft = false;
+ mobileRight();
+ });
+ }
+ }
+
+ // 显示图片预览
+ function imgShow() {
+ $(".message img").click(function () {
+ let src = $(this).attr("src");
+ $("#bigImg").attr("src", src);
+ // 以下代码用于计算并设置图片预览的样式和位置
+ // ...
+ });
+
+ $("#outerImg").click(function () { // 点击外层元素关闭图片预览
+ $(this).fadeOut("fast");
+ });
+ }
+
+ // 获取表情包图片列表
+ function getImageList() {
+ $http.get($constant.baseURL + "/resource/getImageList")
+ .then((res) => {
+ if (!$common.isEmpty(res.data)) {
+ imUtilData.imageList = res.data; // 更新表情包图片列表
+ }
+ })
+ .catch((error) => {
+ ElMessage({
+ message: error.message,
+ type: 'error'
+ });
+ });
+ }
+
+ // 解析消息内容,包括换行符替换和表情/图片解析
+ function parseMessage(content) {
+ content = content.replace(/\n{2,}/g, '
'); // 替换多个换行符
+ content = content.replace(/\n/g, ' '); // 替换单个换行符
+ content = $common.faceReg(content); // 替换表情
+ content = $common.pictureReg(content); // 替换图片链接
+ return content;
+ }
+
+ // 返回对象,供外部使用
+ return {
+ imUtilData,
+ changeAside,
+ mobileRight,
+ getSystemMessages,
+ hiddenBodyLeft, // 建议移除或合并
+ imgShow,
+ getImageList,
+ parseMessage
+ };
+}
+
+
+poetizepoetize-im-uisrccomponentscommonuploadPicture.vue
+
+
+
+
+
+ :action="$store.state.sysConfig.qiniuUrl"
+
+ multiple
+
+ drag
+
+ :on-remove="handleRemove"
+
+ :limit="maxNumber"
+
+ ref="upload"
+
+ :auto-upload="false"
+
+ :http-request="customUpload"
+
+ :on-change="handleChange"
+
+ :before-upload="beforeUpload"
+
+ :on-success="handleSuccess"
+
+ :on-error="handleError"
+
+ list-type="picture"
+
+ accept="image/*">
+
+
+
+
+
+
+
+
+
拖拽上传 / 点击上传
+
+
+
+
+
+ 一次最多上传{{maxNumber}}张图片,且每张图片不超过{{maxSize}}M!
+
+
+
+
+
+
+
+ 上传
+
+
+
+
+
+
+
+
diff --git a/注释(7) b/注释(7)
new file mode 100644
index 0000000..fba7c21
--- /dev/null
+++ b/注释(7)
@@ -0,0 +1,194 @@
+poetizepoetize-im-uisrccomponentscommontreeHole.vue
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{treeHole.createTime}}
+
+
+
+
+
+
+
+
+poetizepoetize-im-uisrccomponentscommonproButton.vue
+
+
+
+
+
+
+
+
+
+
+
+poetizepoetize-im-uisrccomponentscommongroupInfo.vue
+
+
+
+
+
+
+ {{groups[currentGroupId].groupName}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 群名称
+
+
+
+ {{groups[currentGroupId].groupName}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/注释(8) b/注释(8)
new file mode 100644
index 0000000..b573478
--- /dev/null
+++ b/注释(8)
@@ -0,0 +1,139 @@
+
+
+
+
+
+
+ {{groups[currentGroupId].groupName}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 群名称
+
+
+
+ {{groups[currentGroupId].groupName}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/质量分析报告.docx b/质量分析报告.docx
new file mode 100644
index 0000000..830e710
Binary files /dev/null and b/质量分析报告.docx differ