Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
YunaiV 2024-04-23 23:31:58 +08:00
commit dee479ce3c
14 changed files with 192 additions and 234 deletions

View File

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

View File

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

View File

@ -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>

View File

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

View File

@ -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

View File

@ -1,6 +1,7 @@
import { generateUUID } from '@/utils' import { generateUUID } from '@/utils'
import * as DictDataApi from '@/api/system/dict/dict.type' import * as DictDataApi from '@/api/system/dict/dict.type'
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
import selectRule from '@/components/FormCreate/src/config/selectRule'
export const useDictSelectRule = () => { export const useDictSelectRule = () => {
const label = '字典选择器' const label = '字典选择器'
@ -51,73 +52,7 @@ export const useDictSelectRule = () => {
{ label: '布尔值', value: 'bool' } { label: '布尔值', value: 'bool' }
] ]
}, },
{ type: 'switch', field: 'multiple', title: '是否多选' }, ...selectRule
{
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是否在输入框获得焦点后自动弹出选项菜单'
}
]) ])
} }
} }

View File

@ -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: '是否只读' }
])
}
}
}

View File

@ -1,5 +1,6 @@
import { generateUUID } from '@/utils' import { generateUUID } from '@/utils'
import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils' import { localeProps, makeRequiredRule } from '@/components/FormCreate/src/utils'
import selectRule from '@/components/FormCreate/src/config/selectRule'
export const useUserSelectRule = () => { export const useUserSelectRule = () => {
const label = '用户选择器' const label = '用户选择器'
@ -18,76 +19,7 @@ export const useUserSelectRule = () => {
} }
}, },
props(_, { t }) { props(_, { t }) {
return localeProps(t, name + '.props', [ return localeProps(t, name + '.props', [makeRequiredRule(), ...selectRule])
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是否在输入框获得焦点后自动弹出选项菜单'
}
])
} }
} }
} }

View File

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

View File

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

View File

@ -418,3 +418,20 @@ export const erpCalculatePercentage = (value: number, total: number) => {
if (total === 0) return 0 if (total === 0) return 0
return ((value / total) * 100).toFixed(2) 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('省', '')
}

View File

@ -24,6 +24,7 @@ import {
CrmStatisticCustomerAreaRespVO, CrmStatisticCustomerAreaRespVO,
StatisticsPortraitApi StatisticsPortraitApi
} from '@/api/crm/statistics/portrait' } from '@/api/crm/statistics/portrait'
import { areaReplace } from '@/utils'
defineOptions({ name: 'PortraitCustomerArea' }) defineOptions({ name: 'PortraitCustomerArea' })
const props = defineProps<{ queryParams: any }>() // const props = defineProps<{ queryParams: any }>() //
@ -106,12 +107,7 @@ const loadData = async () => {
areaStatisticsList.value = areaList.map((item: CrmStatisticCustomerAreaRespVO) => { areaStatisticsList.value = areaList.map((item: CrmStatisticCustomerAreaRespVO) => {
return { return {
...item, ...item,
areaName: item.areaName // TODO @puhui999, 🤣 mall copy ercharts js areaName: areaReplace(item.areaName)
// .replace('', '')
// .replace('', '')
// .replace('', '')
// .replace('', '')
// .replace('', '')
} }
}) })
buildLeftMap() buildLeftMap()

View File

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

View File

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