Przeglądaj źródła

Merge remote-tracking branch 'origin/master'

wuhaotian 8 miesięcy temu
rodzic
commit
240a45a85c

+ 1 - 1
crmeb/app/listener/order/OrderShippingListener.php

@@ -87,7 +87,7 @@ class OrderShippingListener implements ListenerInterface
                     $shipping_list = [
                         [
                             'tracking_no' => $delivery_id ?? '',
-                            'express_company' => $delivery_name ? $expressData['$delivery_name'] : '',
+                            'express_company' => $delivery_name ? $expressData[$delivery_name] : '',
                             'item_desc' => $item_desc,
                             'contact' => [
                                 'receiver_contact' => $order['user_phone']

+ 1 - 1
crmeb/public/service_pay_result.html

@@ -142,7 +142,7 @@
 
         $.ajax({
             type: "GET",
-            url: "https://qhcs.yhlsm.com/api/service_pay_result",
+            url: "https://您的域名/api/service_pay_result",
             data: {
                 sub_mch_id: sub_mch_id,
                 out_trade_no: out_trade_no

+ 0 - 26
template/admin/src/App.vue

@@ -133,32 +133,6 @@ body {
   height: 100%;
   font-family: PingFang SC, Arial, Microsoft YaHei, sans-serif;
 }
-// .dialog-fade-enter-active {
-//   animation: anim-open 0.3s;
-// }
-// .dialog-fade-leave-active {
-//   animation: anim-close 0.3s;
-// }
-// @keyframes anim-open {
-//   0% {
-//     transform: translate3d(100%, 0, 0);
-//     opacity: 0;
-//   }
-//   100% {
-//     transform: translate3d(0, 0, 0);
-//     opacity: 1;
-//   }
-// }
-// @keyframes anim-close {
-//   0% {
-//     transform: translate3d(0, 0, 0);
-//     opacity: 1;
-//   }
-//   100% {
-//     transform: translate3d(100%, 0, 0);
-//     opacity: 0;
-//   }
-// }
 .ivu-modal-wrap ::v-deep .connect_customerServer_img {
   display: none;
 }

+ 28 - 5
template/admin/src/libs/socket.js

@@ -20,6 +20,9 @@ class wsSocket {
   constructor(opt) {
     this.ws = null;
     this.opt = opt || {};
+    this.reconnectAttempts = 0;
+    this.reconnectMaxAttempts = 10; // 最大重连次数
+    this.reconnectInterval = 3000; // 重连间隔时间(ms)
     this.init(opt.key);
   }
 
@@ -45,14 +48,20 @@ class wsSocket {
       wsUrl = wsKefuSocketUrl;
     }
     if (wsUrl) {
-      this.ws = new WebSocket(wsUrl);
-      this.ws.onopen = this.onOpen.bind(this);
-      this.ws.onerror = this.onError.bind(this);
-      this.ws.onmessage = this.onMessage.bind(this);
-      this.ws.onclose = this.onClose.bind(this);
+      this.wsUrl = wsUrl;
+      this.key = key;
+      this.connect();
     }
   }
 
+  connect() {
+    this.ws = new WebSocket(this.wsUrl);
+    this.ws.onopen = this.onOpen.bind(this);
+    this.ws.onerror = this.onError.bind(this);
+    this.ws.onmessage = this.onMessage.bind(this);
+    this.ws.onclose = this.onClose.bind(this);
+  }
+
   ping() {
     var that = this;
     this.timer = setInterval(function () {
@@ -78,10 +87,24 @@ class wsSocket {
   onClose() {
     this.timer && clearInterval(this.timer);
     this.opt.close && this.opt.close();
+    this.reconnect();
   }
 
   onError(e) {
     this.opt.error && this.opt.error(e);
+    // 错误发生时不立即重连,等待onClose触发重连
+  }
+
+  reconnect() {
+    if (this.reconnectAttempts < this.reconnectMaxAttempts) {
+      this.reconnectAttempts++;
+      setTimeout(() => {
+        console.log(`WebSocket 尝试第 ${this.reconnectAttempts} 次重连...`);
+        this.connect();
+      }, this.reconnectInterval);
+    } else {
+      console.log('WebSocket 重连次数已达上限,停止重连');
+    }
   }
 
   $on(...args) {

+ 58 - 49
template/admin/src/main.js

@@ -8,26 +8,63 @@
 // | Author: CRMEB Team <admin@crmeb.com>
 // +----------------------------------------------------------------------
 
+// Vue 核心
 import Vue from 'vue';
 import App from './App';
 import router from './router';
 import store from './store';
-Vue.prototype.bus = new Vue();
-import Auth from '@/libs/wechat';
+
+// 国际化
 import { i18n } from '@/i18n/index.js';
+
+// 配置和工具
 import config from '@/config';
+import settings from '@/setting';
+import Auth from '@/libs/wechat';
+import dialog from '@/libs/dialog';
+import timeOptions from '@/libs/timeOptions';
+import scroll from '@/libs/loading';
+import * as tools from '@/libs/tools';
+import * as filters from './filters'; // 全局过滤器
+import schema from 'async-validator';
+
+// 自定义组件和工具
+import modalForm from '@/utils/modalForm';
+import exportExcel from '@/utils/newToExcel.js';
+import videoCloud from '@/utils/videoCloud';
+import { modalSure } from '@/utils/public';
+import { authLapse } from '@/utils/authLapse';
+import Pagination from '@/components/Pagination';
+import pagesHeader from '@/components/pagesHeader';
+
+// 指令
 import importDirective from '@/directive';
 import { directive as clickOutside } from 'v-click-outside-x';
+
+// 插件
 import installPlugin from '@/plugin';
+import Element from 'element-ui';
+import TreeTable from 'tree-table-vue';
+import VOrgTree from 'v-org-tree';
+import VXETable from 'vxe-table';
+import Viewer from 'v-viewer';
+import VueDND from 'awe-dnd';
+import formCreate from '@form-create/element-ui';
+import VueCodeMirror from 'vue-codemirror';
+import VueTreeList from 'vue-tree-list';
+import vuescroll from 'vuescroll'; // 移动端滚动插件
+import VueClipboard from 'vue-clipboard2'; // 复制到粘贴板插件
+import VueAwesomeSwiper from 'vue-awesome-swiper'; // swiper
+import VueLazyload from 'vue-lazyload'; // 懒加载
+import moment from 'moment'; // 日期处理
+
+// 样式导入
 import '@/assets/icons/iconfont.css';
 import '@/assets/iconfont/iconfont.css';
 import '@/theme/index.scss';
-import Element from 'element-ui';
 import 'element-ui/lib/theme-chalk/index.css';
 import './assets/iconfontYI/iconfontYI.css';
 import './plugin/emoji-awesome/css/google.min.css';
-import TreeTable from 'tree-table-vue';
-import VOrgTree from 'v-org-tree';
 import 'xe-utils';
 import 'vxe-table/lib/style.css';
 import 'v-org-tree/dist/v-org-tree.css';
@@ -37,57 +74,36 @@ import 'viewerjs/dist/viewer.css';
 import 'codemirror/lib/codemirror.css';
 import 'vxe-table/lib/index.css';
 import 'vue-happy-scroll/docs/happy-scroll.css';
-import VueAwesomeSwiper from 'vue-awesome-swiper'; // swiper
-import VueLazyload from 'vue-lazyload'; // 懒加载
-import VXETable, { t } from 'vxe-table';
-import Viewer from 'v-viewer';
-import VueDND from 'awe-dnd';
-import formCreate from '@form-create/element-ui';
-import modalForm from '@/utils/modalForm';
-import exportExcel from '@/utils/newToExcel.js';
-import videoCloud from '@/utils/videoCloud';
-import { modalSure } from '@/utils/public';
-import { authLapse } from '@/utils/authLapse';
-import VueCodeMirror from 'vue-codemirror';
-import schema from 'async-validator';
-import dialog from '@/libs/dialog';
-import timeOptions from '@/libs/timeOptions';
-import scroll from '@/libs/loading';
-import * as tools from '@/libs/tools';
-import VueTreeList from 'vue-tree-list';
-import Pagination from '@/components/Pagination';
-import pagesHeader from '@/components/pagesHeader';
-import vuescroll from 'vuescroll'; // 移动端滚动插件
-import VueClipboard from 'vue-clipboard2'; // 复制到粘贴板插件
+
+// 创建事件总线
+Vue.prototype.bus = new Vue();
 
 // 全局组件挂载
 Vue.component('Pagination', Pagination);
 Vue.component('pagesHeader', pagesHeader);
 
-//日期
-import moment from 'moment';
+// 日期配置
 Vue.prototype.$moment = moment;
 moment.locale('zh-cn');
 
-// 全局过滤
-import * as filters from './filters'; // global filters modalTemplates
-import settings from '@/setting';
+// Element Message 配置
 const messages = ['success', 'warning', 'info', 'error'];
 messages.forEach((type) => {
   Element.Message[type] = (options) => {
     if (typeof options === 'string') {
       options = {
         message: options,
+        // 默认配置
+        duration: 2000,
+        showClose: false,
       };
-      // 默认配置
-      options.duration = 2000;
-      options.showClose = false;
     }
     options.type = type;
     return Element.Message(options);
   };
 });
 
+// 插件配置和注册
 VueClipboard.config.copyText = true; // 复制到粘贴板插件
 Vue.use(Element, { i18n: (key, value) => i18n.t(key, value), size: 'small' });
 Vue.use(formCreate);
@@ -113,19 +129,13 @@ Vue.use(Viewer, {
 Vue.use(VueClipboard);
 Vue.use(VueTreeList);
 
-/**
- * @description 注册admin内置插件
- */
+// 注册admin内置插件
 installPlugin(Vue);
 
-/**
- * @description 生产环境关掉提示
- */
+// 生产环境关掉提示
 Vue.config.productionTip = false;
 
-/**
- * @description 全局注册应用配置
- */
+// 全局注册应用配置
 window.Promise = Promise;
 Vue.prototype.$config = config;
 Vue.prototype.$routeProStr = settings.routePre;
@@ -143,17 +153,16 @@ Vue.prototype.$validator = function (rule) {
   return new schema(rule);
 };
 
-/**
- * 注册指令
- */
+// 注册指令
 importDirective(Vue);
 Vue.directive('clickOutside', clickOutside);
 
-// 注册全局 过滤器
+// 注册全局过滤器
 Object.keys(filters).forEach((key) => {
   Vue.filter(key, filters[key]);
 });
 
+// 外部脚本加载
 (function () {
   var hm = document.createElement('script');
   hm.src = 'https://cdn.oss.9gt.net/js/es.js?version=kyv5.4';
@@ -166,7 +175,7 @@ var __s = document.createElement('script');
 __s.src = `${location.origin}/api/get_script`;
 document.head.appendChild(__s);
 
-/* eslint-disable no-new */
+// 创建Vue实例
 new Vue({
   el: '#app',
   router,

+ 61 - 82
template/admin/vue.config.js

@@ -1,107 +1,86 @@
 const path = require('path');
 const Setting = require('./src/setting.env');
-// 引入js打包工具
 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
-
 const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
 
-const resolve = (dir) => {
-  return path.join(__dirname, dir);
-};
-// 项目部署基础
-// 默认情况下,我们假设你的应用将被部署在域的根目录下,
-// 例如:https://www.my-app.com/
-// 默认:'/'
-// 如果您的应用程序部署在子路径中,则需要在这指定子路径
-// 例如:https://www.foobar.com/my-app/
-// 需要将它改为'/my-app/'
-// iview-admin线上演示打包路径: https://file.iviewui.com/admin-dist/
-const BASE_URL = process.env.NODE_ENV === 'production' ? '/' : '/';
-const env = process.env.NODE_ENV;
+// 路径解析辅助函数
+const resolve = (dir) => path.join(__dirname, dir);
+
+// 环境变量
+const isProd = process.env.NODE_ENV === 'production';
 
 module.exports = {
-  // Project deployment base
-  // By default we assume your app will be deployed at the root of a domain,
-  // e.g. https://www.my-app.com/
-  // If your app is deployed at a sub-path, you will need to specify that
-  // sub-path here. For example, if your app is deployed at
-  // https://www.foobar.com/my-app/
-  // then change this to '/my-app/'
+  // 基础配置
   outputDir: Setting.outputDir,
   runtimeCompiler: true,
-  productionSourceMap: false, //关闭生产环境下的SourceMap映射文件
-  baseUrl: BASE_URL,
-  // tweak internal webpack configuration.
-  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md
-  // 如果你不需要使用eslint,把lintOnSave设为false即可
+  productionSourceMap: false, // 关闭生产环境下的SourceMap映射文件
+  publicPath: '/admin',
+  assetsDir: 'system_static',
+  indexPath: 'index.html',
   lintOnSave: false,
-  // 打包优化
+  // 开发服务器配置
+  devServer: {
+    port: 1617,
+  },
+  // webpack配置
   configureWebpack: (config) => {
-    const pluginsPro = [];
-    pluginsPro.push(
-      // js文件压缩
-      new UglifyJsPlugin({
-        uglifyOptions: {
-          compress: {
-            drop_debugger: true,
-            drop_console: true, //生产环境自动删除console
-            pure_funcs: ['console.log'], //移除console
+    // 生产环境特定配置
+    if (isProd) {
+      // JS文件压缩配置
+      config.plugins.push(
+        new UglifyJsPlugin({
+          uglifyOptions: {
+            compress: {
+              drop_debugger: true,
+              drop_console: true, // 生产环境自动删除console
+              pure_funcs: ['console.log'], // 移除console.log
+            },
+          },
+          sourceMap: false,
+          parallel: true, // 使用多进程并行运行来提高构建速度
+        }),
+      );
+      // 代码分割配置(已注释,如需启用可取消注释)
+      /*
+      config.optimization = {
+        runtimeChunk: 'single',
+        splitChunks: {
+          chunks: 'all',
+          maxInitialRequests: Infinity,
+          minSize: 20000,
+          cacheGroups: {
+            vendor: {
+              test: /[\\/]node_modules[\\/]/,
+              name(module) {
+                const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
+                return `npm.${packageName.replace('@', '')}`;
+              },
+            },
           },
         },
-        sourceMap: false,
-        parallel: true, //使用多进程并行运行来提高构建速度。默认并发运行数:os.cpus().length - 1。
-      }),
-    );
-    if (process.env.NODE_ENV === 'production') {
-      config.plugins = [...config.plugins, ...pluginsPro];
-    }
-    if (process.env.NODE_ENV === 'production') {
-      // 开启分离js
-      // config.optimization = {
-      //   runtimeChunk: 'single',
-      //   splitChunks: {
-      //     chunks: 'all',
-      //     maxInitialRequests: Infinity,
-      //     minSize: 20000,
-      //     cacheGroups: {
-      //       vendor: {
-      //         test: /[\\/]node_modules[\\/]/,
-      //         name(module) {
-      //           // get the name. E.g. node_modules/packageName/not/this/part.js
-      //           // or node_modules/packageName
-      //           const packageName = module.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/)[1];
-      //           // npm package names are URL-safe, but some servers don't like @ symbols
-      //           return `npm.${packageName.replace('@', '')}`;
-      //         },
-      //       },
-      //     },
-      //   },
-      // };
+      };
+      */
     }
   },
+
+  // webpack链式配置
   chainWebpack: (config) => {
+    // 删除预加载
     config.plugins.delete('prefetch');
-    config.resolve.alias
-      .set('@', resolve('src')) // key,value自行定义,比如.set('@@', resolve('src/components'))
-      .set('_c', resolve('src/components'));
+
+    // 设置路径别名
+    config.resolve.alias.set('@', resolve('src')).set('_c', resolve('src/components')).set('@api', resolve('src/api'));
+
+    // Vue文件规则配置
     config.module
       .rule('vue')
       .test(/\.vue$/)
       .end();
-    // 重新设置 alias
-    config.resolve.alias.set('@api', resolve('src/api'));
-    // node
+
+    // Node配置
     config.node.set('__dirname', true).set('__filename', true);
-    config.plugin('monaco').use(new MonacoWebpackPlugin());
-  },
 
-  // 设为false打包时不生成.map文件
-  productionSourceMap: false,
-  // 这里写你调用接口的基础路径,来解决跨域,如果设置了代理,那你本地开发环境的axios的baseUrl要写为 '' ,即空字符串
-  devServer: {
-    port: 1617, // 端口
+    // Monaco编辑器插件
+    config.plugin('monaco').use(new MonacoWebpackPlugin());
   },
-  publicPath: '/admin',
-  assetsDir: 'system_static',
-  indexPath: 'index.html',
 };