ソースを参照

!432 fix:修复 issues 提到的一些问题
Merge pull request !432 from puhui999/dev-crm

芋道源码 10 ヶ月 前
コミット
95a51ce4f3

+ 0 - 1
package.json

@@ -137,7 +137,6 @@
     "url": "https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues"
   },
   "homepage": "https://gitee.com/yudaocode/yudao-ui-admin-vue3",
-  "packageManager": "pnpm@8.6.0",
   "engines": {
     "node": ">= 16.0.0",
     "pnpm": ">=8.6.0"

+ 1 - 2
src/components/FormCreate/index.ts

@@ -1,4 +1,3 @@
-import MyFormCreateDesigner from './src/MyFormCreateDesigner.vue'
 import { useFormCreateDesigner } from './src/useFormCreateDesigner'
 
-export { MyFormCreateDesigner, useFormCreateDesigner }
+export { useFormCreateDesigner }

+ 0 - 33
src/components/FormCreate/src/MyFormCreateDesigner.vue

@@ -1,33 +0,0 @@
-<!-- TODO puhui999: 没啥问题的话准备移除 -->
-<template>
-  <FcDesigner ref="designer" height="780px" />
-</template>
-
-<script lang="ts" setup>
-import { useUploadFileRule, useUploadImgRule, useUploadImgsRule } from './config'
-
-defineOptions({ name: 'MyFormCreateDesigner' })
-
-const designer = ref() // 表单设计器
-const uploadFileRule = useUploadFileRule()
-const uploadImgRule = useUploadImgRule()
-const uploadImgsRule = useUploadImgsRule()
-
-onMounted(() => {
-  // 移除自带的上传组件规则
-  designer.value?.removeMenuItem('upload')
-  const components = [uploadFileRule, uploadImgRule, uploadImgsRule]
-  components.forEach((component) => {
-    //插入组件规则
-    designer.value?.addComponent(component)
-    //插入拖拽按钮到`main`分类下
-    designer.value?.appendMenuItem('main', {
-      icon: component.icon,
-      name: component.name,
-      label: component.label
-    })
-  })
-})
-</script>
-
-<style lang="scss" scoped></style>

+ 3 - 1
src/components/FormCreate/src/config/index.ts

@@ -3,11 +3,13 @@ import { useUploadImgRule } from './useUploadImgRule'
 import { useUploadImgsRule } from './useUploadImgsRule'
 import { useDictSelectRule } from './useDictSelectRule'
 import { useUserSelectRule } from './useUserSelectRule'
+import { useEditorRule } from './useEditorRule'
 
 export {
   useUploadFileRule,
   useUploadImgRule,
   useUploadImgsRule,
   useDictSelectRule,
-  useUserSelectRule
+  useUserSelectRule,
+  useEditorRule
 }

+ 71 - 0
src/components/FormCreate/src/config/selectRule.ts

@@ -0,0 +1,71 @@
+const selectRule = [
+  { type: 'switch', field: 'multiple', title: '是否多选' },
+  {
+    type: 'switch',
+    field: 'disabled',
+    title: '是否禁用'
+  },
+  { type: 'switch', field: 'clearable', title: '是否可以清空选项' },
+  {
+    type: 'switch',
+    field: 'collapseTags',
+    title: '多选时是否将选中值按文字的形式展示'
+  },
+  {
+    type: 'inputNumber',
+    field: 'multipleLimit',
+    title: '多选时用户最多可以选择的项目数,为 0 则不限制',
+    props: { min: 0 }
+  },
+  {
+    type: 'input',
+    field: 'autocomplete',
+    title: 'autocomplete 属性'
+  },
+  { type: 'input', field: 'placeholder', title: '占位符' },
+  {
+    type: 'switch',
+    field: 'filterable',
+    title: '是否可搜索'
+  },
+  { type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
+  {
+    type: 'input',
+    field: 'noMatchText',
+    title: '搜索条件无匹配时显示的文字'
+  },
+  {
+    type: 'switch',
+    field: 'remote',
+    title: '其中的选项是否从服务器远程加载'
+  },
+  {
+    type: 'Struct',
+    field: 'remoteMethod',
+    title: '自定义远程搜索方法'
+  },
+  { type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
+  {
+    type: 'switch',
+    field: 'reserveKeyword',
+    title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词'
+  },
+  {
+    type: 'switch',
+    field: 'defaultFirstOption',
+    title: '在输入框按下回车,选择第一个匹配项'
+  },
+  {
+    type: 'switch',
+    field: 'popperAppendToBody',
+    title: '是否将弹出框插入至 body 元素',
+    value: true
+  },
+  {
+    type: 'switch',
+    field: 'automaticDropdown',
+    title: '对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单'
+  }
+]
+
+export default selectRule

+ 2 - 67
src/components/FormCreate/src/config/useDictSelectRule.ts

@@ -1,6 +1,7 @@
 import { generateUUID } from '@/utils'
 import * as DictDataApi from '@/api/system/dict/dict.type'
 import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
+import selectRule from '@/components/FormCreate/src/config/selectRule'
 
 export const useDictSelectRule = () => {
   const label = '字典选择器'
@@ -51,73 +52,7 @@ export const useDictSelectRule = () => {
             { label: '布尔值', value: 'bool' }
           ]
         },
-        { type: 'switch', field: 'multiple', title: '是否多选' },
-        {
-          type: 'switch',
-          field: 'disabled',
-          title: '是否禁用'
-        },
-        { type: 'switch', field: 'clearable', title: '是否可以清空选项' },
-        {
-          type: 'switch',
-          field: 'collapseTags',
-          title: '多选时是否将选中值按文字的形式展示'
-        },
-        {
-          type: 'inputNumber',
-          field: 'multipleLimit',
-          title: '多选时用户最多可以选择的项目数,为 0 则不限制',
-          props: { min: 0 }
-        },
-        {
-          type: 'input',
-          field: 'autocomplete',
-          title: 'autocomplete 属性'
-        },
-        { type: 'input', field: 'placeholder', title: '占位符' },
-        {
-          type: 'switch',
-          field: 'filterable',
-          title: '是否可搜索'
-        },
-        { type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
-        {
-          type: 'input',
-          field: 'noMatchText',
-          title: '搜索条件无匹配时显示的文字'
-        },
-        {
-          type: 'switch',
-          field: 'remote',
-          title: '其中的选项是否从服务器远程加载'
-        },
-        {
-          type: 'Struct',
-          field: 'remoteMethod',
-          title: '自定义远程搜索方法'
-        },
-        { type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
-        {
-          type: 'switch',
-          field: 'reserveKeyword',
-          title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词'
-        },
-        {
-          type: 'switch',
-          field: 'defaultFirstOption',
-          title: '在输入框按下回车,选择第一个匹配项'
-        },
-        {
-          type: 'switch',
-          field: 'popperAppendToBody',
-          title: '是否将弹出框插入至 body 元素',
-          value: true
-        },
-        {
-          type: 'switch',
-          field: 'automaticDropdown',
-          title: '对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单'
-        }
+        ...selectRule
       ])
     }
   }

+ 32 - 0
src/components/FormCreate/src/config/useEditorRule.ts

@@ -0,0 +1,32 @@
+import { generateUUID } from '@/utils'
+import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
+
+export const useEditorRule = () => {
+  const label = '富文本'
+  const name = 'Editor'
+  return {
+    icon: 'icon-editor',
+    label,
+    name,
+    rule() {
+      return {
+        type: name,
+        field: generateUUID(),
+        title: label,
+        info: '',
+        $required: false
+      }
+    },
+    props(_, { t }) {
+      return localeProps(t, name + '.props', [
+        makeRequiredRule(),
+        {
+          type: 'input',
+          field: 'height',
+          title: '高度'
+        },
+        { type: 'switch', field: 'readonly', title: '是否只读' }
+      ])
+    }
+  }
+}

+ 2 - 70
src/components/FormCreate/src/config/useUserSelectRule.ts

@@ -1,5 +1,6 @@
 import { generateUUID } from '@/utils'
 import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
+import selectRule from '@/components/FormCreate/src/config/selectRule'
 
 export const useUserSelectRule = () => {
   const label = '用户选择器'
@@ -18,76 +19,7 @@ export const useUserSelectRule = () => {
       }
     },
     props(_, { t }) {
-      return localeProps(t, name + '.props', [
-        makeRequiredRule(),
-        { type: 'switch', field: 'multiple', title: '是否多选' },
-        {
-          type: 'switch',
-          field: 'disabled',
-          title: '是否禁用'
-        },
-        { type: 'switch', field: 'clearable', title: '是否可以清空选项' },
-        {
-          type: 'switch',
-          field: 'collapseTags',
-          title: '多选时是否将选中值按文字的形式展示'
-        },
-        {
-          type: 'inputNumber',
-          field: 'multipleLimit',
-          title: '多选时用户最多可以选择的项目数,为 0 则不限制',
-          props: { min: 0 }
-        },
-        {
-          type: 'input',
-          field: 'autocomplete',
-          title: 'autocomplete 属性'
-        },
-        { type: 'input', field: 'placeholder', title: '占位符' },
-        {
-          type: 'switch',
-          field: 'filterable',
-          title: '是否可搜索'
-        },
-        { type: 'switch', field: 'allowCreate', title: '是否允许用户创建新条目' },
-        {
-          type: 'input',
-          field: 'noMatchText',
-          title: '搜索条件无匹配时显示的文字'
-        },
-        {
-          type: 'switch',
-          field: 'remote',
-          title: '其中的选项是否从服务器远程加载'
-        },
-        {
-          type: 'Struct',
-          field: 'remoteMethod',
-          title: '自定义远程搜索方法'
-        },
-        { type: 'input', field: 'noDataText', title: '选项为空时显示的文字' },
-        {
-          type: 'switch',
-          field: 'reserveKeyword',
-          title: '多选且可搜索时,是否在选中一个选项后保留当前的搜索关键词'
-        },
-        {
-          type: 'switch',
-          field: 'defaultFirstOption',
-          title: '在输入框按下回车,选择第一个匹配项'
-        },
-        {
-          type: 'switch',
-          field: 'popperAppendToBody',
-          title: '是否将弹出框插入至 body 元素',
-          value: true
-        },
-        {
-          type: 'switch',
-          field: 'automaticDropdown',
-          title: '对于不可搜索的 Select,是否在输入框获得焦点后自动弹出选项菜单'
-        }
-      ])
+      return localeProps(t, name + '.props', [makeRequiredRule(), ...selectRule])
     }
   }
 }

+ 8 - 0
src/components/FormCreate/src/useFormCreateDesigner.ts

@@ -1,5 +1,6 @@
 import {
   useDictSelectRule,
+  useEditorRule,
   useUploadFileRule,
   useUploadImgRule,
   useUploadImgsRule,
@@ -13,8 +14,12 @@ import { Ref } from 'vue'
  * - 文件上传
  * - 单图上传
  * - 多图上传
+ * - 字典选择器
+ * - 系统用户选择器
+ * - 富文本
  */
 export const useFormCreateDesigner = (designer: Ref) => {
+  const editorRule = useEditorRule()
   const uploadFileRule = useUploadFileRule()
   const uploadImgRule = useUploadImgRule()
   const uploadImgsRule = useUploadImgsRule()
@@ -24,7 +29,10 @@ export const useFormCreateDesigner = (designer: Ref) => {
   onMounted(() => {
     // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代
     designer.value?.removeMenuItem('upload')
+    // 移除自带的富文本组件规则,使用 editorRule 替代
+    designer.value?.removeMenuItem('fc-editor')
     const components = [
+      editorRule,
       uploadFileRule,
       uploadImgRule,
       uploadImgsRule,

+ 3 - 1
src/plugins/formCreate/index.ts

@@ -21,6 +21,7 @@ import install from '@form-create/element-ui/auto-import'
 import { UploadFile, UploadImg, UploadImgs } from '@/components/UploadFile'
 import { DictSelect } from '@/components/DictSelect'
 import UserSelect from '@/views/system/user/components/UserSelect.vue'
+import { Editor } from '@/components/Editor'
 
 const components = [
   ElAside,
@@ -39,7 +40,8 @@ const components = [
   UploadImgs,
   UploadFile,
   DictSelect,
-  UserSelect
+  UserSelect,
+  Editor
 ]
 
 // 参考 http://www.form-create.com/v3/element-ui/auto-import.html 文档

+ 18 - 1
src/utils/index.ts

@@ -1,4 +1,4 @@
-import { toNumber } from 'lodash-es'
+import {toNumber} from 'lodash-es'
 
 /**
  *
@@ -418,3 +418,20 @@ export const erpCalculatePercentage = (value: number, total: number) => {
   if (total === 0) return 0
   return ((value / total) * 100).toFixed(2)
 }
+
+/**
+ * 适配 echarts map 的地名
+ *
+ * @param areaName 地区名称
+ */
+export const areaReplace = (areaName: string) => {
+  if (!areaName) {
+    return areaName
+  }
+  return areaName
+    .replace('维吾尔自治区', '')
+    .replace('壮族自治区', '')
+    .replace('回族自治区', '')
+    .replace('自治区', '')
+    .replace('省', '')
+}

+ 2 - 6
src/views/crm/statistics/portrait/components/PortraitCustomerArea.vue

@@ -24,6 +24,7 @@ import {
   CrmStatisticCustomerAreaRespVO,
   StatisticsPortraitApi
 } from '@/api/crm/statistics/portrait'
+import { areaReplace } from '@/utils'
 
 defineOptions({ name: 'PortraitCustomerArea' })
 const props = defineProps<{ queryParams: any }>() // 搜索参数
@@ -106,12 +107,7 @@ const loadData = async () => {
   areaStatisticsList.value = areaList.map((item: CrmStatisticCustomerAreaRespVO) => {
     return {
       ...item,
-      areaName: item.areaName // TODO @puhui999:这里最好注释下原因哈, 🤣 我从 mall copy 过来的;这块看着是适合 ercharts 的地名,要不抽个小的 js 方法,然后把涉及到的地方都替换掉。
-      // .replace('维吾尔自治区', '')
-      // .replace('壮族自治区', '')
-      // .replace('回族自治区', '')
-      // .replace('自治区', '')
-      // .replace('省', '')
+      areaName: areaReplace(item.areaName)
     }
   })
   buildLeftMap()

+ 36 - 41
src/views/mall/statistics/member/index.vue

@@ -3,44 +3,44 @@
 
   <div class="flex flex-col">
     <el-row :gutter="16" class="summary">
-      <el-col :sm="6" :xs="12" v-loading="loading">
+      <el-col v-loading="loading" :sm="6" :xs="12">
         <SummaryCard
-          title="累计会员数"
+          :value="summary?.userCount || 0"
           icon="fa-solid:users"
-          icon-color="bg-blue-100"
           icon-bg-color="text-blue-500"
-          :value="summary?.userCount || 0"
+          icon-color="bg-blue-100"
+          title="累计会员数"
         />
       </el-col>
-      <el-col :sm="6" :xs="12" v-loading="loading">
+      <el-col v-loading="loading" :sm="6" :xs="12">
         <SummaryCard
-          title="累计充值人数"
+          :value="summary?.rechargeUserCount || 0"
           icon="fa-solid:user"
-          icon-color="bg-purple-100"
           icon-bg-color="text-purple-500"
-          :value="summary?.rechargeUserCount || 0"
+          icon-color="bg-purple-100"
+          title="累计充值人数"
         />
       </el-col>
-      <el-col :sm="6" :xs="12" v-loading="loading">
+      <el-col v-loading="loading" :sm="6" :xs="12">
         <SummaryCard
-          title="累计充值金额"
+          :decimals="2"
+          :value="fenToYuan(summary?.rechargePrice || 0)"
           icon="fa-solid:money-check-alt"
-          icon-color="bg-yellow-100"
           icon-bg-color="text-yellow-500"
+          icon-color="bg-yellow-100"
           prefix="¥"
-          :decimals="2"
-          :value="fenToYuan(summary?.rechargePrice || 0)"
+          title="累计充值金额"
         />
       </el-col>
-      <el-col :sm="6" :xs="12" v-loading="loading">
+      <el-col v-loading="loading" :sm="6" :xs="12">
         <SummaryCard
-          title="累计消费金额"
+          :decimals="2"
+          :value="fenToYuan(summary?.expensePrice || 0)"
           icon="fa-solid:yen-sign"
-          icon-color="bg-green-100"
           icon-bg-color="text-green-500"
+          icon-color="bg-green-100"
           prefix="¥"
-          :decimals="2"
-          :value="fenToYuan(summary?.expensePrice || 0)"
+          title="累计消费金额"
         />
       </el-col>
     </el-row>
@@ -67,42 +67,42 @@
             <el-col :span="14">
               <el-table :data="areaStatisticsList" :height="300">
                 <el-table-column
-                  label="省份"
-                  prop="areaName"
+                  :sort-method="(obj1, obj2) => obj1.areaName.localeCompare(obj2.areaName, 'zh-CN')"
                   align="center"
+                  label="省份"
                   min-width="80"
+                  prop="areaName"
                   show-overflow-tooltip
                   sortable
-                  :sort-method="(obj1, obj2) => obj1.areaName.localeCompare(obj2.areaName, 'zh-CN')"
                 />
                 <el-table-column
-                  label="会员数量"
-                  prop="userCount"
                   align="center"
+                  label="会员数量"
                   min-width="105"
+                  prop="userCount"
                   sortable
                 />
                 <el-table-column
-                  label="订单创建数量"
-                  prop="orderCreateUserCount"
                   align="center"
+                  label="订单创建数量"
                   min-width="135"
+                  prop="orderCreateUserCount"
                   sortable
                 />
                 <el-table-column
-                  label="订单支付数量"
-                  prop="orderPayUserCount"
                   align="center"
+                  label="订单支付数量"
                   min-width="135"
+                  prop="orderPayUserCount"
                   sortable
                 />
                 <el-table-column
-                  label="订单支付金额"
-                  prop="orderPayPrice"
+                  :formatter="fenToYuanFormat"
                   align="center"
+                  label="订单支付金额"
                   min-width="135"
+                  prop="orderPayPrice"
                   sortable
-                  :formatter="fenToYuanFormat"
                 />
               </el-table>
             </el-col>
@@ -110,7 +110,7 @@
         </el-card>
       </el-col>
       <el-col :md="6" :sm="24">
-        <el-card shadow="never" v-loading="loading">
+        <el-card v-loading="loading" shadow="never">
           <template #header>
             <CardTitle title="会员性别比例" />
           </template>
@@ -122,16 +122,16 @@
 </template>
 <script lang="ts" setup>
 import * as MemberStatisticsApi from '@/api/mall/statistics/member'
-import SummaryCard from '@/components/SummaryCard/index.vue'
-import { EChartsOption } from 'echarts'
-import china from '@/assets/map/json/china.json'
-import { fenToYuan } from '@/utils'
 import {
   MemberAreaStatisticsRespVO,
   MemberSexStatisticsRespVO,
   MemberSummaryRespVO,
   MemberTerminalStatisticsRespVO
 } from '@/api/mall/statistics/member'
+import SummaryCard from '@/components/SummaryCard/index.vue'
+import { EChartsOption } from 'echarts'
+import china from '@/assets/map/json/china.json'
+import { areaReplace, fenToYuan } from '@/utils'
 import { DICT_TYPE, DictDataType, getIntDictOptions } from '@/utils/dict'
 import echarts from '@/plugins/echarts'
 import { fenToYuanFormat } from '@/utils/formatter'
@@ -246,12 +246,7 @@ const getMemberAreaStatisticsList = async () => {
   areaStatisticsList.value = list.map((item: MemberAreaStatisticsRespVO) => {
     return {
       ...item,
-      areaName: item.areaName
-        .replace('维吾尔自治区', '')
-        .replace('壮族自治区', '')
-        .replace('回族自治区', '')
-        .replace('自治区', '')
-        .replace('省', '')
+      areaName: areaReplace(item.areaName)
     }
   })
   let min = 0

+ 8 - 5
src/views/mall/trade/order/form/OrderUpdatePriceForm.vue

@@ -6,7 +6,7 @@
       </el-form-item>
       <el-form-item label="订单调价">
         <el-input-number v-model="formData.adjustPrice" :precision="2" :step="0.1" class="w-100%" />
-        <el-tag class="mt-10px" type="warning">订单调价。 正数,加价;负数,减价</el-tag>
+        <el-tag class="ml-10px" type="warning">订单调价。 正数,加价;负数,减价</el-tag>
       </el-form-item>
       <el-form-item label="调价后">
         <el-input v-model="formData.newPayPrice" disabled />
@@ -38,10 +38,13 @@ const formData = ref({
 })
 watch(
   () => formData.value.adjustPrice,
-  (data: number) => {
-    const num = formData.value.payPrice!.replace('元', '')
-    // @ts-ignore
-    formData.value.newPayPrice = (num * 1 + data).toFixed(2) + '元'
+  (adjustPrice: number | string) => {
+    const numMatch = formData.value.payPrice.match(/\d+(\.\d+)?/)
+    if (numMatch) {
+      const payPriceNum = parseFloat(numMatch[0])
+      adjustPrice = typeof adjustPrice === 'string' ? parseFloat(adjustPrice) : adjustPrice
+      formData.value.newPayPrice = (payPriceNum + adjustPrice).toFixed(2) + '元'
+    }
   }
 )