Browse Source

Merge remote-tracking branch 'yudao/dev' into dev-to-dev

puhui999 1 year ago
parent
commit
39e329b8a3

+ 8 - 7
README.md

@@ -11,6 +11,7 @@
 
 * nodejs > 16.0.0 && pnpm > 7.30.0
 * 演示地址【Vue3 + element-plus】:<http://dashboard-vue3.yudao.iocoder.cn>
+* 演示地址【Vue3 + vben(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn>
 * 演示地址【Vue2 + element-ui】:<http://dashboard.yudao.iocoder.cn>
 * 启动文档:<https://doc.iocoder.cn/quick-start/>
 * 视频教程:<https://doc.iocoder.cn/video/>
@@ -19,8 +20,8 @@
 
 **芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
 
-* 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 
-* 改换saas,自动引入等功能 [vue-element-plus-admin](https://gitee.com/yudaocode/vue-element-plus-admin)
+* 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 实现
+* 改换 saas,自动引入等功能
 * 使用 Element Plus 免费开源的中后台模版,具备如下特性:
 
 ![首页](preview/home.png)
@@ -38,11 +39,11 @@
 | 框架                                                                   | 说明               | 版本     |
 |----------------------------------------------------------------------|------------------|--------|
 | [Vue](https://staging-cn.vuejs.org/)                                 | Vue 框架           | 3.2.47 |
-| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具          | 4.1.4  |
-| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus     | 2.2.34 |
-| [TypeScript](https://www.typescriptlang.org/docs/)                   | JavaScript 的超集   | 4.9.5  |
-| [pinia](https://pinia.vuejs.org/)                                    | Vue 存储库 替代 vuex5 | 2.0.33 |
-| [vueuse](https://vueuse.org/)                                        | 常用工具集            | 9.13.0 |
+| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具          | 4.3.1  |
+| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus     | 2.3.3 |
+| [TypeScript](https://www.typescriptlang.org/docs/)                   | JavaScript 的超集   | 5.0.4  |
+| [pinia](https://pinia.vuejs.org/)                                    | Vue 存储库 替代 vuex5 | 2.0.35 |
+| [vueuse](https://vueuse.org/)                                        | 常用工具集            | 10.1.0 |
 | [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化              | 9.2.2  |
 | [vue-router](https://router.vuejs.org/)                              | Vue 路由           | 4.1.6  |
 | [windicss](https://cn.windicss.org/)                                 | 下一代工具优先的 CSS 框架  | 3.5.6  |

+ 58 - 47
build/vite/optimize.ts

@@ -17,7 +17,6 @@ const include = [
   'cropperjs',
   'lodash-es',
   'nprogress',
-  'animate.css',
   'web-storage-cache',
   '@iconify/iconify',
   '@vueuse/core',
@@ -33,38 +32,58 @@ const include = [
   'element-plus/es',
   'element-plus/es/locale/lang/zh-cn',
   'element-plus/es/locale/lang/en',
-  'element-plus/es/components/backtop/style/index',
-  'element-plus/es/components/form/style/index',
-  'element-plus/es/components/radio-group/style/index',
-  'element-plus/es/components/radio/style/index',
-  'element-plus/es/components/checkbox/style/index',
-  'element-plus/es/components/checkbox-group/style/index',
-  'element-plus/es/components/switch/style/index',
-  'element-plus/es/components/time-picker/style/index',
-  'element-plus/es/components/date-picker/style/index',
-  'element-plus/es/components/col/style/index',
-  'element-plus/es/components/form-item/style/index',
-  'element-plus/es/components/alert/style/index',
-  'element-plus/es/components/breadcrumb/style/index',
-  'element-plus/es/components/select/style/index',
-  'element-plus/es/components/input/style/index',
-  'element-plus/es/components/breadcrumb-item/style/index',
-  'element-plus/es/components/tag/style/index',
-  'element-plus/es/components/pagination/style/index',
-  'element-plus/es/components/table/style/index',
-  'element-plus/es/components/table-column/style/index',
-  'element-plus/es/components/card/style/index',
-  'element-plus/es/components/row/style/index',
-  'element-plus/es/components/button/style/index',
-  'element-plus/es/components/menu/style/index',
-  'element-plus/es/components/sub-menu/style/index',
-  'element-plus/es/components/menu-item/style/index',
-  'element-plus/es/components/option/style/index',
-  'element-plus/es/components/dropdown/style/index',
-  'element-plus/es/components/dropdown-menu/style/index',
-  'element-plus/es/components/dropdown-item/style/index',
-  'element-plus/es/components/skeleton/style/index',
-
+  'element-plus/es/components/backtop/style/css',
+  'element-plus/es/components/form/style/css',
+  'element-plus/es/components/radio-group/style/css',
+  'element-plus/es/components/radio/style/css',
+  'element-plus/es/components/checkbox/style/css',
+  'element-plus/es/components/checkbox-group/style/css',
+  'element-plus/es/components/switch/style/css',
+  'element-plus/es/components/time-picker/style/css',
+  'element-plus/es/components/date-picker/style/css',
+  'element-plus/es/components/descriptions/style/css',
+  'element-plus/es/components/descriptions-item/style/css',
+  'element-plus/es/components/link/style/css',
+  'element-plus/es/components/tooltip/style/css',
+  'element-plus/es/components/drawer/style/css',
+  'element-plus/es/components/dialog/style/css',
+  'element-plus/es/components/checkbox-button/style/css',
+  'element-plus/es/components/option-group/style/css',
+  'element-plus/es/components/radio-button/style/css',
+  'element-plus/es/components/cascader/style/css',
+  'element-plus/es/components/color-picker/style/css',
+  'element-plus/es/components/input-number/style/css',
+  'element-plus/es/components/rate/style/css',
+  'element-plus/es/components/select-v2/style/css',
+  'element-plus/es/components/tree-select/style/css',
+  'element-plus/es/components/slider/style/css',
+  'element-plus/es/components/time-select/style/css',
+  'element-plus/es/components/autocomplete/style/css',
+  'element-plus/es/components/image-viewer/style/css',
+  'element-plus/es/components/upload/style/css',
+  'element-plus/es/components/col/style/css',
+  'element-plus/es/components/form-item/style/css',
+  'element-plus/es/components/alert/style/css',
+  'element-plus/es/components/breadcrumb/style/css',
+  'element-plus/es/components/select/style/css',
+  'element-plus/es/components/input/style/css',
+  'element-plus/es/components/breadcrumb-item/style/css',
+  'element-plus/es/components/tag/style/css',
+  'element-plus/es/components/pagination/style/css',
+  'element-plus/es/components/table/style/css',
+  'element-plus/es/components/table-v2/style/css',
+  'element-plus/es/components/table-column/style/css',
+  'element-plus/es/components/card/style/css',
+  'element-plus/es/components/row/style/css',
+  'element-plus/es/components/button/style/css',
+  'element-plus/es/components/menu/style/css',
+  'element-plus/es/components/sub-menu/style/css',
+  'element-plus/es/components/menu-item/style/css',
+  'element-plus/es/components/option/style/css',
+  'element-plus/es/components/dropdown/style/css',
+  'element-plus/es/components/dropdown-menu/style/css',
+  'element-plus/es/components/dropdown-item/style/css',
+  'element-plus/es/components/skeleton/style/css',
   'element-plus/es/components/skeleton/style/css',
   'element-plus/es/components/backtop/style/css',
   'element-plus/es/components/menu/style/css',
@@ -78,20 +97,12 @@ const include = [
   'element-plus/es/components/breadcrumb/style/css',
   'element-plus/es/components/breadcrumb-item/style/css',
   'element-plus/es/components/image/style/css',
-  'element-plus/es/components/tag/style/css',
-  'element-plus/es/components/dialog/style/css',
-  'element-plus/es/components/form/style/css',
-  'element-plus/es/components/form-item/style/css',
-  'element-plus/es/components/card/style/css',
-  'element-plus/es/components/tooltip/style/css',
-  'element-plus/es/components/radio-group/style/css',
-  'element-plus/es/components/radio/style/css',
-  'element-plus/es/components/input-number/style/css',
-  'element-plus/es/components/tree-select/style/css',
-  'element-plus/es/components/drawer/style/css',
-  'element-plus/es/components/image-viewer/style/css',
-  'element-plus/es/components/upload/style/css',
-  'element-plus/es/components/switch/style/css'
+  'element-plus/es/components/collapse-transition/style/css',
+  'element-plus/es/components/timeline/style/css',
+  'element-plus/es/components/timeline-item/style/css',
+  'element-plus/es/components/collapse/style/css',
+  'element-plus/es/components/collapse-item/style/css',
+  'element-plus/es/components/button-group/style/css'
 ]
 
 const exclude = ['@iconify/json']

+ 33 - 34
package.json

@@ -33,12 +33,12 @@
     "@form-create/element-ui": "^3.1.17",
     "@iconify/iconify": "^3.1.0",
     "@videojs-player/vue": "^1.0.0",
-    "@vueuse/core": "^9.13.0",
+    "@vueuse/core": "^10.1.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
     "@zxcvbn-ts/core": "^2.2.1",
     "animate.css": "^4.1.1",
-    "axios": "^1.3.5",
+    "axios": "^1.3.6",
     "benz-amr-recorder": "^1.1.5",
     "bpmn-js-token-simulation": "^0.10.0",
     "camunda-bpmn-moddle": "^7.0.1",
@@ -46,19 +46,19 @@
     "crypto-js": "^4.1.1",
     "dayjs": "^1.11.7",
     "diagram-js": "^11.6.0",
-    "echarts": "^5.4.1",
+    "echarts": "^5.4.2",
     "echarts-wordcloud": "^2.1.0",
     "element-plus": "2.3.3",
-    "fast-xml-parser": "^4.1.3",
+    "fast-xml-parser": "^4.2.2",
     "highlight.js": "^11.7.0",
     "intro.js": "^7.0.1",
     "jsencrypt": "^3.3.2",
     "lodash-es": "^4.17.21",
-    "min-dash": "^4.0.0",
+    "min-dash": "^4.1.0",
     "mitt": "^3.0.0",
     "nprogress": "^0.2.0",
-    "pinia": "^2.0.34",
-    "qrcode": "^1.5.1",
+    "pinia": "^2.0.35",
+    "qrcode": "^1.5.3",
     "qs": "^6.11.1",
     "steady-xml": "^0.1.0",
     "url": "^0.11.0",
@@ -73,61 +73,60 @@
     "xml-js": "^1.6.11"
   },
   "devDependencies": {
-    "@commitlint/cli": "^17.5.0",
-    "@commitlint/config-conventional": "^17.4.4",
-    "@iconify/json": "^2.2.38",
+    "@commitlint/cli": "^17.6.1",
+    "@commitlint/config-conventional": "^17.6.1",
+    "@iconify/json": "^2.2.54",
     "@intlify/unplugin-vue-i18n": "^0.10.0",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.1",
     "@types/lodash-es": "^4.17.7",
-    "@types/node": "^18.15.5",
+    "@types/node": "^18.16.0",
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.5.0",
     "@types/qs": "^6.9.7",
-    "@typescript-eslint/eslint-plugin": "^5.56.0",
-    "@typescript-eslint/parser": "^5.56.0",
+    "@typescript-eslint/eslint-plugin": "^5.59.0",
+    "@typescript-eslint/parser": "^5.59.0",
     "@vitejs/plugin-legacy": "^4.0.2",
     "@vitejs/plugin-vue": "^4.1.0",
     "@vitejs/plugin-vue-jsx": "^3.0.1",
     "autoprefixer": "^10.4.14",
     "bpmn-js": "^8.9.0",
     "bpmn-js-properties-panel": "^0.46.0",
-    "consola": "^2.15.3",
-    "eslint": "^8.36.0",
+    "consola": "^3.1.0",
+    "eslint": "^8.39.0",
     "eslint-config-prettier": "^8.8.0",
-    "eslint-define-config": "^1.17.0",
+    "eslint-define-config": "^1.18.0",
     "eslint-plugin-prettier": "^4.2.1",
-    "eslint-plugin-vue": "^9.9.0",
-    "lint-staged": "^13.2.0",
-    "postcss": "^8.4.21",
+    "eslint-plugin-vue": "^9.11.0",
+    "lint-staged": "^13.2.1",
+    "postcss": "^8.4.23",
     "postcss-html": "^1.5.0",
     "postcss-scss": "^4.0.6",
-    "prettier": "^2.8.6",
-    "rimraf": "^4.4.1",
-    "rollup": "^3.20.0",
-    "sass": "^1.59.3",
-    "stylelint": "^15.3.0",
+    "prettier": "^2.8.8",
+    "rimraf": "^5.0.0",
+    "rollup": "^3.20.7",
+    "sass": "^1.62.0",
+    "stylelint": "^15.6.0",
     "stylelint-config-html": "^1.1.0",
-    "stylelint-config-prettier": "^9.0.5",
-    "stylelint-config-recommended": "^11.0.0",
-    "stylelint-config-standard": "^31.0.0",
+    "stylelint-config-recommended": "^12.0.0",
+    "stylelint-config-standard": "^33.0.0",
     "stylelint-order": "^6.0.3",
-    "terser": "^5.16.6",
-    "typescript": "5.0.2",
-    "unplugin-auto-import": "^0.15.1",
-    "unplugin-element-plus": "^0.7.0",
+    "terser": "^5.17.1",
+    "typescript": "5.0.4",
+    "unplugin-auto-import": "^0.15.3",
+    "unplugin-element-plus": "^0.7.1",
     "unplugin-vue-components": "^0.24.1",
-    "vite": "4.2.1",
+    "vite": "4.3.1",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "^1.6.4",
     "vite-plugin-eslint": "^1.8.1",
-    "vite-plugin-progress": "^0.0.6",
+    "vite-plugin-progress": "^0.0.7",
     "vite-plugin-purge-icons": "^0.9.2",
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-top-level-await": "^1.3.0",
     "vite-plugin-vue-setup-extend-plus": "^0.1.0",
     "vite-plugin-windicss": "^1.8.10",
-    "vue-tsc": "^1.2.0",
+    "vue-tsc": "^1.4.4",
     "windicss": "^3.5.6"
   },
   "engines": {

+ 40 - 0
src/api/mall/trade/delivery/express/index.ts

@@ -0,0 +1,40 @@
+import request from '@/config/axios'
+
+export interface DeliveryExpressVO {
+  id: number
+  code: string
+  name: string
+  logo: string
+  sort: number
+  status: number
+}
+
+// 查询快递公司列表
+export const getDeliveryExpressPage = async (params: PageParam) => {
+  return await request.get({ url: '/trade/delivery/express/page', params })
+}
+
+// 查询快递公司详情
+export const getDeliveryExpress = async (id: number) => {
+  return await request.get({ url: '/trade/delivery/express/get?id=' + id })
+}
+
+// 新增快递公司
+export const createDeliveryExpress = async (data: DeliveryExpressVO) => {
+  return await request.post({ url: '/trade/delivery/express/create', data })
+}
+
+// 修改快递公司
+export const updateDeliveryExpress = async (data: DeliveryExpressVO) => {
+  return await request.put({ url: '/trade/delivery/express/update', data })
+}
+
+// 删除快递公司
+export const deleteDeliveryExpress = async (id: number) => {
+  return await request.delete({ url: '/trade/delivery/express/delete?id=' + id })
+}
+
+// 导出快递公司 Excel
+export const exportDeliveryExpressApi = async (params) => {
+  return await request.download({ url: '/trade/delivery/express/export-excel', params })
+}

+ 1 - 1
src/api/system/notify/template/index.ts

@@ -39,7 +39,7 @@ export const updateNotifyTemplate = async (data: NotifyTemplateVO) => {
 }
 
 // 删除站内信模板
-export const deleteNotifyTemplateApi = async (id: number) => {
+export const deleteNotifyTemplate = async (id: number) => {
   return await request.delete({ url: '/system/notify-template/delete?id=' + id })
 }
 

+ 1 - 1
src/api/system/oauth2/token.ts

@@ -17,6 +17,6 @@ export const getAccessTokenPage = (params: PageParam) => {
 }
 
 // 删除 token
-export const deleteAccessToken = (accessToken: number) => {
+export const deleteAccessToken = (accessToken: string) => {
   return request.delete({ url: '/system/oauth2-token/delete?accessToken=' + accessToken })
 }

+ 1 - 1
src/views/bpm/definition/index.vue

@@ -87,7 +87,7 @@
     <MyProcessViewer
       key="designer"
       v-model="bpmnXML"
-      :value="bpmnXML"
+      :value="bpmnXML as any"
       v-bind="bpmnControlForm"
       :prefix="bpmnControlForm.prefix"
     />

+ 1 - 1
src/views/bpm/group/UserGroupForm.vue

@@ -68,7 +68,7 @@ const formRules = reactive({
   status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
 })
 const formRef = ref() // 表单 Ref
-const userList = ref([]) // 用户列表
+const userList = ref<any[]>([]) // 用户列表
 
 /** 打开弹窗 */
 const open = async (type: string, id?: number) => {

+ 2 - 1
src/views/bpm/group/index.vue

@@ -117,6 +117,7 @@ import { dateFormatter } from '@/utils/formatTime'
 import * as UserGroupApi from '@/api/bpm/userGroup'
 import * as UserApi from '@/api/system/user'
 import UserGroupForm from './UserGroupForm.vue'
+import { UserVO } from '@/api/system/user'
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 
@@ -131,7 +132,7 @@ const queryParams = reactive({
   createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
-const userList = ref([]) // 用户列表
+const userList = ref<UserVO[]>([]) // 用户列表
 
 /** 查询列表 */
 const getList = async () => {

+ 1 - 1
src/views/bpm/model/editor/index.vue

@@ -16,7 +16,7 @@
     <!-- 流程属性器,负责编辑每个流程节点的属性 -->
     <MyProcessPenal
       key="penal"
-      :bpmnModeler="modeler"
+      :bpmnModeler="modeler as any"
       :prefix="controlForm.prefix"
       class="process-panel"
       :model="model"

+ 1 - 1
src/views/bpm/model/index.vue

@@ -219,7 +219,7 @@
     <MyProcessViewer
       key="designer"
       v-model="bpmnXML"
-      :value="bpmnXML"
+      :value="bpmnXML as any"
       v-bind="bpmnControlForm"
       :prefix="bpmnControlForm.prefix"
     />

+ 1 - 1
src/views/bpm/oa/leave/detail.vue

@@ -28,7 +28,7 @@ const props = defineProps({
   id: propTypes.number.def(undefined)
 })
 const detailLoading = ref(false) // 表单的加载中
-const detailData = ref({}) // 详情数据
+const detailData = ref<any>({}) // 详情数据
 const queryId = query.id as unknown as number // 从 URL 传递过来的 id 编号
 
 /** 获得数据 */

+ 1 - 1
src/views/bpm/processInstance/create/index.vue

@@ -43,7 +43,7 @@
       </el-col>
     </el-card>
     <!-- 流程图预览 -->
-    <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML" />
+    <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML as any" />
   </ContentWrap>
 </template>
 <script setup lang="ts" name="BpmProcessInstanceCreate">

+ 6 - 4
src/views/infra/build/index.vue

@@ -33,6 +33,12 @@
 import FcDesigner from '@form-create/designer'
 import { useClipboard } from '@vueuse/core'
 import { isString } from '@/utils/is'
+
+import hljs from 'highlight.js' // 导入代码高亮文件
+import 'highlight.js/styles/github.css' // 导入代码高亮样式
+import xml from 'highlight.js/lib/languages/java'
+import json from 'highlight.js/lib/languages/json'
+
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息
 
@@ -112,10 +118,6 @@ const copy = async (text: string) => {
 /**
  * 代码高亮
  */
-import hljs from 'highlight.js' // 导入代码高亮文件
-import 'highlight.js/styles/github.css' // 导入代码高亮样式
-import xml from 'highlight.js/lib/languages/java'
-import json from 'highlight.js/lib/languages/json'
 const highlightedCode = (code) => {
   // 处理语言和代码
   let language = 'json'

+ 8 - 7
src/views/infra/codegen/PreviewCode.vue

@@ -59,6 +59,14 @@ import { useClipboard } from '@vueuse/core'
 import { handleTree2 } from '@/utils/tree'
 import * as CodegenApi from '@/api/infra/codegen'
 
+import hljs from 'highlight.js' // 导入代码高亮文件
+import 'highlight.js/styles/github.css' // 导入代码高亮样式
+import java from 'highlight.js/lib/languages/java'
+import xml from 'highlight.js/lib/languages/java'
+import javascript from 'highlight.js/lib/languages/javascript'
+import sql from 'highlight.js/lib/languages/sql'
+import typescript from 'highlight.js/lib/languages/typescript'
+
 const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
 
@@ -184,13 +192,6 @@ const copy = async (text: string) => {
 /**
  * 代码高亮
  */
-import hljs from 'highlight.js' // 导入代码高亮文件
-import 'highlight.js/styles/github.css' // 导入代码高亮样式
-import java from 'highlight.js/lib/languages/java'
-import xml from 'highlight.js/lib/languages/java'
-import javascript from 'highlight.js/lib/languages/javascript'
-import sql from 'highlight.js/lib/languages/sql'
-import typescript from 'highlight.js/lib/languages/typescript'
 const highlightedCode = (item) => {
   const language = item.filePath.substring(item.filePath.lastIndexOf('.') + 1)
   const result = hljs.highlight(language, item.code || '', true)

+ 188 - 92
src/views/infra/redis/index.vue

@@ -1,7 +1,6 @@
 <template>
   <doc-alert title="Redis 缓存" url="https://doc.iocoder.cn/redis-cache/" />
   <doc-alert title="本地缓存" url="https://doc.iocoder.cn/local-cache/" />
-
   <el-scrollbar height="calc(100vh - 88px - 40px - 50px)">
     <el-row>
       <!-- 基本信息 -->
@@ -51,127 +50,224 @@
       <!-- 命令统计 -->
       <el-col :span="12" class="mt-3">
         <el-card :gutter="12" shadow="hover">
-          <div ref="commandStatsRef" class="h-88"></div>
+          <Echart :options="commandStatsRefChika" :height="420" />
         </el-card>
       </el-col>
       <!-- 内存使用量统计 -->
       <el-col :span="12" class="mt-3">
         <el-card class="ml-3" :gutter="12" shadow="hover">
-          <div ref="usedmemory" class="h-88"></div>
+          <Echart :options="usedmemoryEchartChika" :height="420" />
         </el-card>
       </el-col>
     </el-row>
   </el-scrollbar>
 </template>
-<script setup lang="ts" name="InfraRedis">
-import * as echarts from 'echarts'
+<script setup lang="ts">
+import echarts from '@/plugins/echarts'
+import { GaugeChart } from 'echarts/charts'
+import { ToolboxComponent } from 'echarts/components'
 import * as RedisApi from '@/api/infra/redis'
 import { RedisMonitorInfoVO } from '@/api/infra/redis/types'
-
 const cache = ref<RedisMonitorInfoVO>()
 
 // 基本信息
 const readRedisInfo = async () => {
   const data = await RedisApi.getCache()
   cache.value = data
-  loadEchartOptions(data.commandStats)
 }
-// 图表
-const commandStatsRef = ref<HTMLElement>()
-const usedmemory = ref<HTMLDivElement>()
-
-const loadEchartOptions = (stats) => {
-  const commandStats = [] as any[]
-  const nameList = [] as string[]
-  stats.forEach((row) => {
-    commandStats.push({
-      name: row.command,
-      value: row.calls
-    })
-    nameList.push(row.command)
-  })
-
-  const commandStatsInstance = echarts.init(commandStatsRef.value!, 'macarons')
 
-  commandStatsInstance.setOption({
-    title: {
-      text: '命令统计',
-      left: 'center'
-    },
-    tooltip: {
-      trigger: 'item',
-      formatter: '{a} <br/>{b} : {c} ({d}%)'
-    },
-    legend: {
-      type: 'scroll',
-      orient: 'vertical',
-      right: 30,
-      top: 10,
-      bottom: 20,
-      data: nameList,
-      textStyle: {
-        color: '#a1a1a1'
+// 内存使用情况
+const usedmemoryEchartChika = reactive({
+  title: {
+    // 仪表盘标题。
+    text: '内存使用情况',
+    left: 'center',
+    show: true, // 是否显示标题,默认 true。
+    offsetCenter: [0, '20%'], //相对于仪表盘中心的偏移位置,数组第一项是水平方向的偏移,第二项是垂直方向的偏移。可以是绝对的数值,也可以是相对于仪表盘半径的百分比。
+    color: 'yellow', // 文字的颜色,默认 #333。
+    fontSize: 20 // 文字的字体大小,默认 15。
+  },
+  toolbox: {
+    show: false,
+    feature: {
+      restore: { show: true },
+      saveAsImage: { show: true }
+    }
+  },
+  series: [
+    {
+      name: '峰值',
+      type: 'gauge',
+      min: 0,
+      max: 50,
+      splitNumber: 10,
+      //这是指针的颜色
+      color: '#F5C74E',
+      radius: '85%',
+      center: ['50%', '50%'],
+      startAngle: 225,
+      endAngle: -45,
+      axisLine: {
+        // 坐标轴线
+        lineStyle: {
+          // 属性lineStyle控制线条样式
+          color: [
+            [0.2, '#7FFF00'],
+            [0.8, '#00FFFF'],
+            [1, '#FF0000']
+          ],
+          //width: 6 外框的大小(环的宽度)
+          width: 10
+        }
+      },
+      axisTick: {
+        // 坐标轴小标记
+        //里面的线长是5(短线)
+        length: 5, // 属性length控制线长
+        lineStyle: {
+          // 属性lineStyle控制线条样式
+          color: '#76D9D7'
+        }
+      },
+      splitLine: {
+        // 分隔线
+        length: 20, // 属性length控制线长
+        lineStyle: {
+          // 属性lineStyle(详见lineStyle)控制线条样式
+          color: '#76D9D7'
+        }
+      },
+      axisLabel: {
+        color: '#76D9D7',
+        distance: 15,
+        fontSize: 15
+      },
+      pointer: {
+        // 指针的大小
+        width: 7,
+        show: true
+      },
+      detail: {
+        textStyle: {
+          fontWeight: 'normal',
+          // 里面文字下的数值大小(50)
+          fontSize: 15,
+          color: '#FFFFFF'
+        },
+        valueAnimation: true
+      },
+      progress: {
+        show: true
       }
-    },
-    series: [
-      {
-        name: '命令',
-        type: 'pie',
-        radius: [20, 120],
-        center: ['40%', '60%'],
-        data: commandStats,
-        roseType: 'radius',
+    }
+  ]
+})
+
+// 指令使用情况
+const commandStatsRefChika = reactive({
+  title: {
+    text: '命令统计',
+    left: 'center'
+  },
+  tooltip: {
+    trigger: 'item',
+    formatter: '{a} <br/>{b} : {c} ({d}%)'
+  },
+  legend: {
+    type: 'scroll',
+    orient: 'vertical',
+    right: 30,
+    top: 10,
+    bottom: 20,
+    data: [] as any[],
+    textStyle: {
+      color: '#a1a1a1'
+    }
+  },
+  series: [
+    {
+      name: '命令',
+      type: 'pie',
+      radius: [20, 120],
+      center: ['40%', '60%'],
+      data: [] as any[],
+      roseType: 'radius',
+      label: {
+        show: true
+      },
+      emphasis: {
         label: {
           show: true
         },
-        emphasis: {
-          label: {
-            show: true
-          },
-          itemStyle: {
-            shadowBlur: 10,
-            shadowOffsetX: 0,
-            shadowColor: 'rgba(0, 0, 0, 0.5)'
-          }
+        itemStyle: {
+          shadowBlur: 10,
+          shadowOffsetX: 0,
+          shadowColor: 'rgba(0, 0, 0, 0.5)'
         }
       }
-    ]
-  })
+    }
+  ]
+})
+
+/** 加载数据 */
+const getSummary = () => {
+  // 初始化命令图表
+  initCommandStatsChart()
+  usedMemoryInstance()
+}
+
+/** 命令使用情况 */
+const initCommandStatsChart = async () => {
+  usedmemoryEchartChika.series[0].data = []
+  // 发起请求
+  try {
+    const data = await RedisApi.getCache()
+    cache.value = data
+    // 处理数据
+    const commandStats = [] as any[]
+    const nameList = [] as string[]
+    data.commandStats.forEach((row) => {
+      commandStats.push({
+        name: row.command,
+        value: row.calls
+      })
+      nameList.push(row.command)
+    })
+    commandStatsRefChika.legend.data = nameList
+    commandStatsRefChika.series[0].data = commandStats
+  } catch {}
+}
+const usedMemoryInstance = async () => {
+  try {
+    const data = await RedisApi.getCache()
+    cache.value = data
+    // 仪表盘详情,用于显示数据。
+    usedmemoryEchartChika.series[0].detail = {
+      show: true, // 是否显示详情,默认 true。
+      offsetCenter: [0, '50%'], // 相对于仪表盘中心的偏移位置,数组第一项是水平方向的偏移,第二项是垂直方向的偏移。可以是绝对的数值,也可以是相对于仪表盘半径的百分比。
+      color: 'auto', // 文字的颜色,默认 auto。
+      fontSize: 30, // 文字的字体大小,默认 15。
+      formatter: cache.value!.info.used_memory_human // 格式化函数或者字符串
+    }
 
-  const usedMemoryInstance = echarts.init(usedmemory.value!, 'macarons')
-  usedMemoryInstance.setOption({
-    title: {
-      text: '内存使用情况',
-      left: 'center'
-    },
-    tooltip: {
+    usedmemoryEchartChika.series[0].data[0] = {
+      value: cache.value!.info.used_memory_human,
+      name: '内存消耗'
+    }
+    console.log(cache.value!.info)
+    usedmemoryEchartChika.tooltip = {
       formatter: '{b} <br/>{a} : ' + cache.value!.info.used_memory_human
-    },
-    series: [
-      {
-        name: '峰值',
-        type: 'gauge',
-        min: 0,
-        max: 100,
-        progress: {
-          show: true
-        },
-        detail: {
-          formatter: cache.value!.info.used_memory_human
-        },
-        data: [
-          {
-            value: parseFloat(cache.value!.info.used_memory_human),
-            name: '内存消耗'
-          }
-        ]
-      }
-    ]
-  })
+    }
+  } catch {}
 }
 
-onBeforeMount(() => {
-  // TODO @hiiwbs 微信,优化使用 Echart 组件
+/** 初始化 **/
+onMounted(() => {
+  echarts.use([ToolboxComponent])
+  echarts.use([GaugeChart])
+  // 读取 redis 信息
   readRedisInfo()
+  // 加载数据
+  getSummary()
 })
 </script>

+ 125 - 0
src/views/mall/trade/delivery/express/ExpressForm.vue

@@ -0,0 +1,125 @@
+<template>
+  <Dialog :title="dialogTitle" v-model="dialogVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="120px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="快递公司编码" prop="code">
+        <el-input v-model="formData.code" placeholder="请输入快递编码" />
+      </el-form-item>
+      <el-form-item label="快递公司名称" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入快递名称" />
+      </el-form-item>
+      <el-form-item label="快递公司logo" prop="logo">
+        <UploadImg v-model="formData.logo" :limit="1" :is-show-tip="false" />
+        <div style="font-size: 10px" class="pl-10px">推荐 180x180 图片分辨率</div>
+      </el-form-item>
+      <el-form-item label="分类排序" prop="sort">
+        <el-input-number v-model="formData.sort" controls-position="right" :min="0" />
+      </el-form-item>
+      <el-form-item label="开启状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.value"
+          >
+            {{ dict.label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="dialogVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+
+<script setup lang="ts" name="ExpressForm">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { CommonStatusEnum } from '@/utils/constants'
+import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const dialogVisible = ref(false) // 弹窗的是否展示
+const dialogTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  code: '',
+  name: '',
+  logo: '',
+  sort: 0,
+  status: CommonStatusEnum.ENABLE
+})
+const formRules = reactive({
+  code: [{ required: true, message: '快递编码不能为空', trigger: 'blur' }],
+  name: [{ required: true, message: '分类名称不能为空', trigger: 'blur' }],
+  logo: [{ required: true, message: '分类图片不能为空', trigger: 'blur' }],
+  sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  dialogVisible.value = true
+  dialogTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await DeliveryExpressApi.getDeliveryExpress(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as DeliveryExpressApi.DeliveryExpressVO
+    if (formType.value === 'create') {
+      await DeliveryExpressApi.createDeliveryExpress(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await DeliveryExpressApi.updateDeliveryExpress(data)
+      message.success(t('common.updateSuccess'))
+    }
+    dialogVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    name: '',
+    picUrl: '',
+    bigPicUrl: '',
+    status: CommonStatusEnum.ENABLE
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 183 - 0
src/views/mall/trade/delivery/express/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <!-- 搜索工作栏 -->
+  <ContentWrap>
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="100px"
+    >
+      <el-form-item label="快递公司编码" prop="code">
+        <el-input
+          v-model="queryParams.code"
+          placeholder="请输快递公司编码"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item label="快递公司名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输快递公司名称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
+        />
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['trade:delivery:express:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px" /> 新增
+        </el-button>
+        <el-button
+          type="success"
+          plain
+          @click="handleExport"
+          :loading="exportLoading"
+          v-hasPermi="['trade:delivery:express:export']"
+        >
+          <Icon icon="ep:download" class="mr-5px" /> 导出
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="快递公司编号" prop="code" />
+      <el-table-column label="快递公司名称" prop="name" />
+      <el-table-column label="快递公司 logo " prop="logo">
+        <template #default="scope">
+          <img v-if="scope.row.logo" :src="scope.row.logo" alt="快递公司logo" class="h-25px" />
+        </template>
+      </el-table-column>
+      <el-table-column label="排序" align="center" prop="sort" />
+      <el-table-column label="开启状态" align="center" prop="status">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        width="180"
+        :formatter="dateFormatter"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['trade:delivery:express:update']"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['trade:delivery:express:delete']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <ExpressForm ref="formRef" @success="getList" />
+</template>
+<script setup lang="ts" name="Express">
+import { DICT_TYPE } from '@/utils/dict'
+import { dateFormatter } from '@/utils/formatTime'
+import download from '@/utils/download'
+import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
+import ExpressForm from './ExpressForm.vue'
+const message = useMessage() // 消息弹窗
+const { t } = useI18n() // 国际化
+const total = ref(0) // 列表的总页数
+const loading = ref(true) // 列表的加载中
+const list = ref<any[]>([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  code: '',
+  name: ''
+})
+const queryFormRef = ref() // 搜索的表单
+const exportLoading = ref(false) // 导出的加载中
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await DeliveryExpressApi.getDeliveryExpressPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await DeliveryExpressApi.deleteDeliveryExpress(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 导出按钮操作 */
+const handleExport = async () => {
+  try {
+    // 导出的二次确认
+    await message.exportConfirm()
+    // 发起导出
+    exportLoading.value = true
+    const data = await DeliveryExpressApi.exportDeliveryExpressApi(queryParams)
+    download.excel(data, '快递公司.xls')
+  } catch {
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
+</script>

+ 78 - 0
src/views/mp/autoReply/components/ReplyForm.vue

@@ -0,0 +1,78 @@
+<template>
+  <div>
+    <el-form ref="formRef" :model="replyForm" :rules="rules" label-width="80px">
+      <el-form-item label="消息类型" prop="requestMessageType" v-if="msgType === MsgType.Message">
+        <el-select v-model="replyForm.requestMessageType" placeholder="请选择">
+          <template v-for="dict in getDictOptions(DICT_TYPE.MP_MESSAGE_TYPE)" :key="dict.value">
+            <el-option
+              v-if="RequestMessageTypes.includes(dict.value)"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </template>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="匹配类型" prop="requestMatch" v-if="msgType === MsgType.Keyword">
+        <el-select v-model="replyForm.requestMatch" placeholder="请选择匹配类型" clearable>
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="关键词" prop="requestKeyword" v-if="msgType === MsgType.Keyword">
+        <el-input v-model="replyForm.requestKeyword" placeholder="请输入内容" clearable />
+      </el-form-item>
+      <el-form-item label="回复消息">
+        <WxReplySelect v-model="reply" />
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script setup lang="ts" name="ReplyForm">
+import WxReplySelect, { type Reply } from '@/views/mp/components/wx-reply'
+import type { FormInstance } from 'element-plus'
+import { MsgType } from './types'
+import { DICT_TYPE, getDictOptions, getIntDictOptions } from '@/utils/dict'
+
+const props = defineProps<{
+  modelValue: any
+  reply: Reply
+  msgType: MsgType
+}>()
+
+const emit = defineEmits<{
+  (e: 'update:reply', v: Reply)
+  (e: 'update:modelValue', v: any)
+}>()
+
+const reply = computed<Reply>({
+  get: () => props.reply,
+  set: (val) => emit('update:reply', val)
+})
+
+const replyForm = computed<any>({
+  get: () => props.modelValue,
+  set: (val) => emit('update:modelValue', val)
+})
+
+const formRef = ref<FormInstance | null>(null) // 表单 ref
+
+const RequestMessageTypes = ['text', 'image', 'voice', 'video', 'shortvideo', 'location', 'link'] // 允许选择的请求消息类型
+
+// 表单校验
+const rules = {
+  requestKeyword: [{ required: true, message: '请求的关键字不能为空', trigger: 'blur' }],
+  requestMatch: [{ required: true, message: '请求的关键字的匹配不能为空', trigger: 'blur' }]
+}
+
+defineExpose({
+  resetFields: () => formRef.value?.resetFields(),
+  validate: async () => formRef.value?.validate()
+})
+</script>
+
+<style scoped></style>

+ 20 - 52
src/views/mp/autoReply/index.vue

@@ -53,38 +53,13 @@
       @on-delete="onDelete"
     />
 
-    <!-- 添加或修改自动回复的对话框 -->
-    <!-- TODO @Dhb52 -->
-    <el-dialog :title="dialogTitle" v-model="showFormDialog" width="800px" destroy-on-close>
-      <el-form ref="formRef" :model="replyForm" :rules="rules" label-width="80px">
-        <el-form-item label="消息类型" prop="requestMessageType" v-if="msgType === MsgType.Message">
-          <el-select v-model="replyForm.requestMessageType" placeholder="请选择">
-            <template v-for="dict in getDictOptions(DICT_TYPE.MP_MESSAGE_TYPE)" :key="dict.value">
-              <el-option
-                v-if="RequestMessageTypes.includes(dict.value)"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </template>
-          </el-select>
-        </el-form-item>
-        <el-form-item label="匹配类型" prop="requestMatch" v-if="msgType === MsgType.Keyword">
-          <el-select v-model="replyForm.requestMatch" placeholder="请选择匹配类型" clearable>
-            <el-option
-              v-for="dict in getIntDictOptions(DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH)"
-              :key="dict.value"
-              :label="dict.label"
-              :value="dict.value"
-            />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="关键词" prop="requestKeyword" v-if="msgType === MsgType.Keyword">
-          <el-input v-model="replyForm.requestKeyword" placeholder="请输入内容" clearable />
-        </el-form-item>
-        <el-form-item label="回复消息">
-          <WxReplySelect v-model="reply" />
-        </el-form-item>
-      </el-form>
+    <el-dialog
+      :title="isCreating ? '新增自动回复' : '修改自动回复'"
+      v-model="showDialog"
+      width="800px"
+      destroy-on-close
+    >
+      <ReplyForm v-model="replyForm" v-model:reply="reply" :msg-type="msgType" ref="formRef" />
       <template #footer>
         <el-button @click="cancel">取 消</el-button>
         <el-button type="primary" @click="onSubmit">确 定</el-button>
@@ -93,23 +68,22 @@
   </ContentWrap>
 </template>
 <script setup lang="ts" name="MpAutoReply">
-import WxReplySelect, { type Reply, ReplyType } from '@/views/mp/components/wx-reply'
+import ReplyForm from '@/views/mp/autoReply/components/ReplyForm.vue'
+import { type Reply, ReplyType } from '@/views/mp/components/wx-reply'
 import WxAccountSelect from '@/views/mp/components/wx-account-select'
 import * as MpAutoReplyApi from '@/api/mp/autoReply'
-import { DICT_TYPE, getDictOptions, getIntDictOptions } from '@/utils/dict'
 import { ContentWrap } from '@/components/ContentWrap'
-import type { FormInstance, TabPaneName } from 'element-plus'
+import type { TabPaneName } from 'element-plus'
 import ReplyTable from './components/ReplyTable.vue'
 import { MsgType } from './components/types'
 const message = useMessage() // 消息
 
 const accountId = ref(-1) // 公众号ID
 const msgType = ref<MsgType>(MsgType.Keyword) // 消息类型
-const RequestMessageTypes = ['text', 'image', 'voice', 'video', 'shortvideo', 'location', 'link'] // 允许选择的请求消息类型
 const loading = ref(true) // 遮罩层
 const total = ref(0) // 总条数
 const list = ref<any[]>([]) // 自动回复列表
-const formRef = ref<FormInstance | null>(null) // 表单 ref
+const formRef = ref<InstanceType<typeof ReplyForm> | null>(null) // 表单 ref
 // 查询参数
 const queryParams = reactive({
   pageNo: 1,
@@ -117,19 +91,14 @@ const queryParams = reactive({
   accountId: accountId
 })
 
-const dialogTitle = ref('') // 弹出层标题
-const showFormDialog = ref(false) // 是否显示弹出层
+const isCreating = ref(false) // 是否新建(否则编辑)
+const showDialog = ref(false) // 是否显示弹出层
 const replyForm = ref<any>({}) // 表单参数
 // 回复消息
 const reply = ref<Reply>({
   type: ReplyType.Text,
   accountId: -1
 })
-// 表单校验
-const rules = {
-  requestKeyword: [{ required: true, message: '请求的关键字不能为空', trigger: 'blur' }],
-  requestMatch: [{ required: true, message: '请求的关键字的匹配不能为空', trigger: 'blur' }]
-}
 
 /** 侦听账号变化 */
 const onAccountChanged = (id: number) => {
@@ -174,8 +143,8 @@ const onCreate = () => {
     accountId: queryParams.accountId
   }
 
-  dialogTitle.value = '新增自动回复'
-  showFormDialog.value = true
+  isCreating.value = true
+  showDialog.value = true
 }
 
 /** 修改按钮操作 */
@@ -207,8 +176,8 @@ const onUpdate = async (id: number) => {
   }
 
   // 打开表单
-  dialogTitle.value = '修改自动回复'
-  showFormDialog.value = true
+  isCreating.value = false
+  showDialog.value = true
 }
 
 /** 删除按钮操作 */
@@ -220,8 +189,7 @@ const onDelete = async (id: number) => {
 }
 
 const onSubmit = async () => {
-  const valid = await formRef.value?.validate()
-  if (!valid) return
+  await formRef.value?.validate()
 
   // 处理回复消息
   const submitForm: any = { ...replyForm.value }
@@ -245,7 +213,7 @@ const onSubmit = async () => {
     message.success('新增成功')
   }
 
-  showFormDialog.value = false
+  showDialog.value = false
   await getList()
 }
 
@@ -264,7 +232,7 @@ const reset = () => {
 
 // 取消按钮
 const cancel = () => {
-  showFormDialog.value = false
+  showDialog.value = false
   reset()
 }
 </script>

+ 3 - 1
src/views/system/dict/data/DictDataForm.vue

@@ -122,7 +122,9 @@ const open = async (type: string, id?: number, dictType?: string) => {
   dialogTitle.value = t('action.' + type)
   formType.value = type
   resetForm()
-  formData.value.dictType = dictType
+  if (dictType) {
+    formData.value.dictType = dictType
+  }
   // 修改时,设置数据
   if (id) {
     formLoading.value = true

+ 4 - 4
src/views/system/notice/NoticeForm.vue

@@ -17,9 +17,9 @@
         <el-select v-model="formData.type" clearable placeholder="请选择公告类型">
           <el-option
             v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_NOTICE_TYPE)"
-            :key="parseInt(dict.value)"
+            :key="parseInt(dict.value as any)"
             :label="dict.label"
-            :value="parseInt(dict.value)"
+            :value="parseInt(dict.value as any)"
           />
         </el-select>
       </el-form-item>
@@ -27,9 +27,9 @@
         <el-select v-model="formData.status" clearable placeholder="请选择状态">
           <el-option
             v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-            :key="parseInt(dict.value)"
+            :key="parseInt(dict.value as any)"
             :label="dict.label"
-            :value="parseInt(dict.value)"
+            :value="parseInt(dict.value as any)"
           />
         </el-select>
       </el-form-item>

+ 3 - 4
src/views/system/notify/template/NotifyTemplateSendForm.vue

@@ -44,7 +44,6 @@
   </Dialog>
 </template>
 <script lang="ts" name="SystemNotifyTemplateSendForm" setup>
-import * as SmsTemplateApi from '@/api/system/sms/smsTemplate'
 import * as UserApi from '@/api/system/user'
 import * as NotifyTemplateApi from '@/api/system/notify/template'
 const message = useMessage() // 消息弹窗
@@ -102,8 +101,8 @@ const submitForm = async () => {
   // 提交请求
   formLoading.value = true
   try {
-    const data = formData.value as SmsTemplateApi.SendSmsReqVO
-    const logId = await SmsTemplateApi.sendSms(data)
+    const data = formData.value as unknown as NotifyTemplateApi.NotifySendReqVO
+    const logId = await NotifyTemplateApi.sendNotify(data)
     if (logId) {
       message.success('提交发送成功!发送结果,见发送日志编号:' + logId)
     }
@@ -121,7 +120,7 @@ const resetForm = () => {
     mobile: '',
     templateCode: '',
     templateParams: new Map()
-  }
+  } as any
   formRef.value?.resetFields()
 }
 </script>

+ 3 - 3
src/views/system/oauth2/token/index.vue

@@ -80,7 +80,7 @@
           <el-button
             link
             type="danger"
-            @click="handleForceLogout(scope.row.id)"
+            @click="handleForceLogout(scope.row.accessToken)"
             v-hasPermi="['system:oauth2-token:delete']"
           >
             强退
@@ -142,12 +142,12 @@ const resetQuery = () => {
 }
 
 /** 强制退出操作 */
-const handleForceLogout = async (id: number) => {
+const handleForceLogout = async (accessToken: string) => {
   try {
     // 删除的二次确认
     await message.confirm('是否要强制退出用户')
     // 发起删除
-    await OAuth2AccessTokenApi.deleteAccessToken(id)
+    await OAuth2AccessTokenApi.deleteAccessToken(accessToken)
     message.success(t('common.success'))
     // 刷新列表
     await getList()

+ 1 - 1
src/views/system/post/PostForm.vue

@@ -117,7 +117,7 @@ const resetForm = () => {
     sort: undefined,
     status: CommonStatusEnum.ENABLE,
     remark: ''
-  }
+  } as any
   formRef.value?.resetFields()
 }
 </script>

+ 1 - 1
stylelint.config.js

@@ -2,7 +2,7 @@ module.exports = {
   root: true,
   plugins: ['stylelint-order'],
   customSyntax: 'postcss-html',
-  extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
+  extends: ['stylelint-config-standard'],
   rules: {
     'selector-pseudo-class-no-unknown': [
       true,