diff --git a/crmeb/app/listener/order/OrderShippingListener.php b/crmeb/app/listener/order/OrderShippingListener.php index 47103e374..d0577f73f 100644 --- a/crmeb/app/listener/order/OrderShippingListener.php +++ b/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'] diff --git a/crmeb/public/service_pay_result.html b/crmeb/public/service_pay_result.html index b903d7582..3512dafaa 100644 --- a/crmeb/public/service_pay_result.html +++ b/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 diff --git a/template/admin/src/App.vue b/template/admin/src/App.vue index 48a2fcf89..de9ab3b46 100644 --- a/template/admin/src/App.vue +++ b/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; } diff --git a/template/admin/src/libs/socket.js b/template/admin/src/libs/socket.js index 67a374e19..f4c40f9f5 100644 --- a/template/admin/src/libs/socket.js +++ b/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) { diff --git a/template/admin/src/main.js b/template/admin/src/main.js index 32cc1fe4e..a91d03712 100644 --- a/template/admin/src/main.js +++ b/template/admin/src/main.js @@ -8,26 +8,63 @@ // | Author: CRMEB Team // +---------------------------------------------------------------------- +// 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, diff --git a/template/admin/vue.config.js b/template/admin/vue.config.js index 2d8598557..4b215a518 100644 --- a/template/admin/vue.config.js +++ b/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); + + // Monaco编辑器插件 config.plugin('monaco').use(new MonacoWebpackPlugin()); }, - - // 设为false打包时不生成.map文件 - productionSourceMap: false, - // 这里写你调用接口的基础路径,来解决跨域,如果设置了代理,那你本地开发环境的axios的baseUrl要写为 '' ,即空字符串 - devServer: { - port: 1617, // 端口 - }, - publicPath: '/admin', - assetsDir: 'system_static', - indexPath: 'index.html', };