Merge remote-tracking branch 'upload/master'

pull/551/head
痴货 2024-08-23 09:52:07 +08:00
commit da32360570
157 changed files with 1391 additions and 511 deletions

View File

@ -83,7 +83,8 @@
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
}, },
"[vue]": { "[vue]": {
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint" "editor.defaultFormatter": "rvest.vs-code-prettier-eslint"

View File

@ -47,7 +47,7 @@
"driver.js": "^1.3.1", "driver.js": "^1.3.1",
"echarts": "^5.5.0", "echarts": "^5.5.0",
"echarts-wordcloud": "^2.1.0", "echarts-wordcloud": "^2.1.0",
"element-plus": "2.7.0", "element-plus": "2.8.0",
"fast-xml-parser": "^4.3.2", "fast-xml-parser": "^4.3.2",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"jsencrypt": "^3.3.2", "jsencrypt": "^3.3.2",

View File

@ -1,7 +1,20 @@
import { getAccessToken } from '@/utils/auth' import { getAccessToken } from '@/utils/auth'
import { fetchEventSource } from '@microsoft/fetch-event-source' import { fetchEventSource } from '@microsoft/fetch-event-source'
import { config } from '@/config/axios/config' import { config } from '@/config/axios/config'
import request from '@/config/axios'
// AI 思维导图 VO
export interface MindMapVO {
id: number // 编号
userId: number // 用户编号
prompt: string // 生成内容提示
generatedContent: string // 生成的思维导图内容
platform: string // 平台
model: string // 模型
errorMessage: string // 错误信息
}
// AI 思维导图生成 VO
export interface AiMindMapGenerateReqVO { export interface AiMindMapGenerateReqVO {
prompt: string prompt: string
} }
@ -34,5 +47,14 @@ export const AiMindMapApi = {
onclose: onClose, onclose: onClose,
signal: ctrl.signal signal: ctrl.signal
}) })
},
// 查询思维导图分页
getMindMapPage: async (params: any) => {
return await request.get({ url: `/ai/mind-map/page`, params })
},
// 删除思维导图
deleteMindMap: async (id: number) => {
return await request.delete({ url: `/ai/mind-map/delete?id=` + id })
} }
} }

View File

@ -24,20 +24,6 @@ export interface PropertyValueVO {
remark?: string remark?: string
} }
/**
*
*/
export interface PropertyValueDetailVO {
/** 属性项的编号 */
propertyId: number // 属性的编号
/** 属性的名称 */
propertyName: string
/** 属性值的编号 */
valueId: number
/** 属性值的名称 */
valueName: string
}
// ------------------------ 属性项 ------------------- // ------------------------ 属性项 -------------------
// 创建属性项 // 创建属性项
@ -65,6 +51,11 @@ export const getPropertyPage = (params: PageParam) => {
return request.get({ url: '/product/property/page', params }) return request.get({ url: '/product/property/page', params })
} }
// 获得属性项精简列表
export const getPropertySimpleList = (): Promise<PropertyVO[]> => {
return request.get({ url: '/product/property/simple-list' })
}
// ------------------------ 属性值 ------------------- // ------------------------ 属性值 -------------------
// 获得属性值分页 // 获得属性值分页
@ -91,3 +82,8 @@ export const updatePropertyValue = (data: PropertyValueVO) => {
export const deletePropertyValue = (id: number) => { export const deletePropertyValue = (id: number) => {
return request.delete({ url: `/product/property/value/delete?id=${id}` }) return request.delete({ url: `/product/property/value/delete?id=${id}` })
} }
// 获得属性值精简列表
export const getPropertyValueSimpleList = (propertyId: number): Promise<PropertyValueVO[]> => {
return request.get({ url: '/product/property/value/simple-list', params: { propertyId } })
}

View File

@ -50,6 +50,8 @@ export interface Spu {
giveIntegral?: number // 赠送积分 giveIntegral?: number // 赠送积分
virtualSalesCount?: number // 虚拟销量 virtualSalesCount?: number // 虚拟销量
price?: number // 商品价格 price?: number // 商品价格
combinationPrice?: number // 商品拼团价格
seckillPrice?: number // 商品秒杀价格
salesCount?: number // 商品销量 salesCount?: number // 商品销量
marketPrice?: number // 市场价 marketPrice?: number // 市场价
costPrice?: number // 成本价 costPrice?: number // 成本价

View File

@ -24,6 +24,7 @@ export interface SeckillActivityVO {
// 秒杀活动所需属性 // 秒杀活动所需属性
export interface SeckillProductVO { export interface SeckillProductVO {
skuId: number skuId: number
spuId: number
seckillPrice: number seckillPrice: number
stock: number stock: number
} }

View File

@ -2,6 +2,7 @@ import request from '@/config/axios'
export interface AppVO { export interface AppVO {
id: number id: number
appKey: string
name: string name: string
status: number status: number
remark: string remark: string

View File

@ -548,10 +548,10 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.second.type"> <el-radio-group v-model="cronValue.second.type">
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.second.type == '1'" label="范围"> <el-form-item v-if="cronValue.second.type == '1'" label="范围">
@ -607,10 +607,10 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.minute.type"> <el-radio-group v-model="cronValue.minute.type">
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.minute.type == '1'" label="范围"> <el-form-item v-if="cronValue.minute.type == '1'" label="范围">
@ -666,10 +666,10 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.hour.type"> <el-radio-group v-model="cronValue.hour.type">
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.hour.type == '1'" label="范围"> <el-form-item v-if="cronValue.hour.type == '1'" label="范围">
@ -725,12 +725,12 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.day.type"> <el-radio-group v-model="cronValue.day.type">
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
<el-radio-button label="4">本月最后一天</el-radio-button> <el-radio-button value="4">本月最后一天</el-radio-button>
<el-radio-button label="5">不指定</el-radio-button> <el-radio-button value="5">不指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.day.type == '1'" label="范围"> <el-form-item v-if="cronValue.day.type == '1'" label="范围">
@ -786,10 +786,10 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.month.type"> <el-radio-group v-model="cronValue.month.type">
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.month.type == '1'" label="范围"> <el-form-item v-if="cronValue.month.type == '1'" label="范围">
@ -846,12 +846,12 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.week.type"> <el-radio-group v-model="cronValue.week.type">
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
<el-radio-button label="4">本月最后一周</el-radio-button> <el-radio-button value="4">本月最后一周</el-radio-button>
<el-radio-button label="5">不指定</el-radio-button> <el-radio-button value="5">不指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.week.type == '1'" label="范围"> <el-form-item v-if="cronValue.week.type == '1'" label="范围">
@ -925,11 +925,11 @@ const inputChange = () => {
<el-form> <el-form>
<el-form-item label="类型"> <el-form-item label="类型">
<el-radio-group v-model="cronValue.year.type"> <el-radio-group v-model="cronValue.year.type">
<el-radio-button label="-1">忽略</el-radio-button> <el-radio-button value="-1">忽略</el-radio-button>
<el-radio-button label="0">任意值</el-radio-button> <el-radio-button value="0">任意值</el-radio-button>
<el-radio-button label="1">范围</el-radio-button> <el-radio-button value="1">范围</el-radio-button>
<el-radio-button label="2">间隔</el-radio-button> <el-radio-button value="2">间隔</el-radio-button>
<el-radio-button label="3">指定</el-radio-button> <el-radio-button value="3">指定</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="cronValue.year.type == '1'" label="范围"> <el-form-item v-if="cronValue.year.type == '1'" label="范围">

View File

@ -1,8 +1,9 @@
<script lang="tsx"> <script lang="tsx">
import { defineComponent, PropType, ref } from 'vue' import { defineComponent, PropType, computed } from 'vue'
import { isHexColor } from '@/utils/color' import { isHexColor } from '@/utils/color'
import { ElTag } from 'element-plus' import { ElTag } from 'element-plus'
import { DictDataType, getDictOptions } from '@/utils/dict' import { DictDataType, getDictOptions } from '@/utils/dict'
import { isArray, isString, isNumber, isBoolean } from '@/utils/is'
export default defineComponent({ export default defineComponent({
name: 'DictTag', name: 'DictTag',
@ -12,49 +13,78 @@ export default defineComponent({
required: true required: true
}, },
value: { value: {
type: [String, Number, Boolean] as PropType<string | number | boolean>, type: [String, Number, Boolean, Array],
required: true required: true
},
// props.value
separator: {
type: String as PropType<string>,
default: ','
},
// tag 5px el-row gutter
gutter: {
type: String as PropType<string>,
default: '5px'
} }
}, },
setup(props) { setup(props) {
const dictData = ref<DictDataType>() const valueArr: any = computed(() => {
const getDictObj = (dictType: string, value: string) => { // 1. Number Boolean
const dictOptions = getDictOptions(dictType) if (isNumber(props.value) || isBoolean(props.value)) {
dictOptions.forEach((dict: DictDataType) => { return [String(props.value)]
if (dict.value === value) { }
if (dict.colorType + '' === 'default') { // 2. -> props.sepSymbol
dict.colorType = 'info' else if (isString(props.value)) {
} return props.value.split(props.separator)
dictData.value = dict }
} // 3.
}) else if (isArray(props.value)) {
} return props.value.map(String)
const rederDictTag = () => { }
return []
})
const renderDictTag = () => {
if (!props.type) { if (!props.type) {
return null return null
} }
// //
if (props.value === undefined || props.value === null) { if (props.value === undefined || props.value === null || props.value === '') {
return null return null
} }
getDictObj(props.type, props.value.toString()) const dictOptions = getDictOptions(props.type)
//
return ( return (
<ElTag <div
style={dictData.value?.cssClass ? 'color: #fff' : ''} class="dict-tag"
type={dictData.value?.colorType} style={{
color={ display: 'inline-flex',
dictData.value?.cssClass && isHexColor(dictData.value?.cssClass) gap: props.gutter,
? dictData.value?.cssClass justifyContent: 'center',
: '' alignItems: 'center'
} }}
disableTransitions={true}
> >
{dictData.value?.label} {dictOptions.map((dict: DictDataType) => {
</ElTag> if (valueArr.value.includes(dict.value)) {
if (dict.colorType + '' === 'primary' || dict.colorType + '' === 'default') {
dict.colorType = ''
}
return (
//
<ElTag
style={dict?.cssClass ? 'color: #fff' : ''}
type={dict?.colorType || null}
color={dict?.cssClass && isHexColor(dict?.cssClass) ? dict?.cssClass : ''}
disableTransitions={true}
>
{dict?.label}
</ElTag>
)
}
})}
</div>
) )
} }
return () => rederDictTag() return () => renderDictTag()
} }
}) })
</script> </script>

View File

@ -11,8 +11,8 @@
<el-form :model="formData" label-width="80px"> <el-form :model="formData" label-width="80px">
<el-form-item label="组件背景" prop="bgType"> <el-form-item label="组件背景" prop="bgType">
<el-radio-group v-model="formData.bgType"> <el-radio-group v-model="formData.bgType">
<el-radio label="color">纯色</el-radio> <el-radio value="color">纯色</el-radio>
<el-radio label="img">图片</el-radio> <el-radio value="img">图片</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'"> <el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'">

View File

@ -5,12 +5,12 @@
<el-form-item label="样式" prop="type"> <el-form-item label="样式" prop="type">
<el-radio-group v-model="formData.type"> <el-radio-group v-model="formData.type">
<el-tooltip class="item" content="默认" placement="bottom"> <el-tooltip class="item" content="默认" placement="bottom">
<el-radio-button label="default"> <el-radio-button value="default">
<Icon icon="system-uicons:carousel" /> <Icon icon="system-uicons:carousel" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="卡片" placement="bottom"> <el-tooltip class="item" content="卡片" placement="bottom">
<el-radio-button label="card"> <el-radio-button value="card">
<Icon icon="ic:round-view-carousel" /> <Icon icon="ic:round-view-carousel" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
@ -18,8 +18,8 @@
</el-form-item> </el-form-item>
<el-form-item label="指示器" prop="indicator"> <el-form-item label="指示器" prop="indicator">
<el-radio-group v-model="formData.indicator"> <el-radio-group v-model="formData.indicator">
<el-radio label="dot">小圆点</el-radio> <el-radio value="dot">小圆点</el-radio>
<el-radio label="number">数字</el-radio> <el-radio value="number">数字</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="是否轮播" prop="autoplay"> <el-form-item label="是否轮播" prop="autoplay">
@ -43,8 +43,8 @@
<template #default="{ element }"> <template #default="{ element }">
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px"> <el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
<el-radio-group v-model="element.type"> <el-radio-group v-model="element.type">
<el-radio label="img">图片</el-radio> <el-radio value="img">图片</el-radio>
<el-radio label="video">视频</el-radio> <el-radio value="video">视频</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item

View File

@ -26,17 +26,17 @@
<el-form-item label="列数" prop="type"> <el-form-item label="列数" prop="type">
<el-radio-group v-model="formData.columns"> <el-radio-group v-model="formData.columns">
<el-tooltip class="item" content="一列" placement="bottom"> <el-tooltip class="item" content="一列" placement="bottom">
<el-radio-button :label="1"> <el-radio-button :value="1">
<Icon icon="fluent:text-column-one-24-filled" /> <Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="二列" placement="bottom"> <el-tooltip class="item" content="二列" placement="bottom">
<el-radio-button :label="2"> <el-radio-button :value="2">
<Icon icon="fluent:text-column-two-24-filled" /> <Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="三列" placement="bottom"> <el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button :label="3"> <el-radio-button :value="3">
<Icon icon="fluent:text-column-three-24-filled" /> <Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>

View File

@ -11,7 +11,7 @@
:key="index" :key="index"
:content="item.text" :content="item.text"
> >
<el-radio-button :label="item.type"> <el-radio-button :value="item.type">
<Icon :icon="item.icon" /> <Icon :icon="item.icon" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
@ -24,12 +24,12 @@
<el-form-item label="左右边距" prop="paddingType"> <el-form-item label="左右边距" prop="paddingType">
<el-radio-group v-model="formData!.paddingType"> <el-radio-group v-model="formData!.paddingType">
<el-tooltip content="无边距" placement="top"> <el-tooltip content="无边距" placement="top">
<el-radio-button label="none"> <el-radio-button value="none">
<Icon icon="tabler:box-padding" /> <Icon icon="tabler:box-padding" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="左右留边" placement="top"> <el-tooltip content="左右留边" placement="top">
<el-radio-button label="horizontal"> <el-radio-button value="horizontal">
<Icon icon="vaadin:padding" /> <Icon icon="vaadin:padding" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>

View File

@ -3,8 +3,8 @@
<el-card header="按钮配置" class="property-group" shadow="never"> <el-card header="按钮配置" class="property-group" shadow="never">
<el-form-item label="展开方向" prop="direction"> <el-form-item label="展开方向" prop="direction">
<el-radio-group v-model="formData.direction"> <el-radio-group v-model="formData.direction">
<el-radio label="vertical">垂直</el-radio> <el-radio value="vertical">垂直</el-radio>
<el-radio label="horizontal">水平</el-radio> <el-radio value="horizontal">水平</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="显示文字" prop="showText"> <el-form-item label="显示文字" prop="showText">

View File

@ -4,8 +4,8 @@
<el-form label-width="80px" :model="formData" class="m-t-8px"> <el-form label-width="80px" :model="formData" class="m-t-8px">
<el-form-item label="每行数量" prop="column"> <el-form-item label="每行数量" prop="column">
<el-radio-group v-model="formData.column"> <el-radio-group v-model="formData.column">
<el-radio :label="3">3</el-radio> <el-radio :value="3">3</el-radio>
<el-radio :label="4">4</el-radio> <el-radio :value="4">4</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>

View File

@ -4,21 +4,21 @@
<el-form label-width="80px" :model="formData" class="m-t-8px"> <el-form label-width="80px" :model="formData" class="m-t-8px">
<el-form-item label="布局" prop="layout"> <el-form-item label="布局" prop="layout">
<el-radio-group v-model="formData.layout"> <el-radio-group v-model="formData.layout">
<el-radio label="iconText">图标+文字</el-radio> <el-radio value="iconText">图标+文字</el-radio>
<el-radio label="icon">仅图标</el-radio> <el-radio value="icon">仅图标</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="行数" prop="row"> <el-form-item label="行数" prop="row">
<el-radio-group v-model="formData.row"> <el-radio-group v-model="formData.row">
<el-radio :label="1">1</el-radio> <el-radio :value="1">1</el-radio>
<el-radio :label="2">2</el-radio> <el-radio :value="2">2</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="列数" prop="column"> <el-form-item label="列数" prop="column">
<el-radio-group v-model="formData.column"> <el-radio-group v-model="formData.column">
<el-radio :label="3">3</el-radio> <el-radio :value="3">3</el-radio>
<el-radio :label="4">4</el-radio> <el-radio :value="4">4</el-radio>
<el-radio :label="5">5</el-radio> <el-radio :value="5">5</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>

View File

@ -14,9 +14,9 @@
<template v-if="selectedHotAreaIndex === cellIndex"> <template v-if="selectedHotAreaIndex === cellIndex">
<el-form-item label="类型" :prop="`cell[${cellIndex}].type`"> <el-form-item label="类型" :prop="`cell[${cellIndex}].type`">
<el-radio-group v-model="cell.type"> <el-radio-group v-model="cell.type">
<el-radio label="text">文字</el-radio> <el-radio value="text">文字</el-radio>
<el-radio label="image">图片</el-radio> <el-radio value="image">图片</el-radio>
<el-radio label="search">搜索框</el-radio> <el-radio value="search">搜索框</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 1. 文字 --> <!-- 1. 文字 -->

View File

@ -2,27 +2,27 @@
<el-form label-width="80px" :model="formData" :rules="rules"> <el-form label-width="80px" :model="formData" :rules="rules">
<el-form-item label="样式" prop="styleType"> <el-form-item label="样式" prop="styleType">
<el-radio-group v-model="formData!.styleType"> <el-radio-group v-model="formData!.styleType">
<el-radio label="normal">标准</el-radio> <el-radio value="normal">标准</el-radio>
<el-tooltip <el-tooltip
content="沉侵式头部仅支持微信小程序、APP建议页面第一个组件为图片展示类组件" content="沉侵式头部仅支持微信小程序、APP建议页面第一个组件为图片展示类组件"
placement="top" placement="top"
> >
<el-radio label="inner">沉浸式</el-radio> <el-radio value="inner">沉浸式</el-radio>
</el-tooltip> </el-tooltip>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'inner'"> <el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'inner'">
<el-radio-group v-model="formData!.alwaysShow"> <el-radio-group v-model="formData!.alwaysShow">
<el-radio :label="false">关闭</el-radio> <el-radio :value="false">关闭</el-radio>
<el-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top"> <el-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top">
<el-radio :label="true">开启</el-radio> <el-radio :value="true">开启</el-radio>
</el-tooltip> </el-tooltip>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="背景类型" prop="bgType"> <el-form-item label="背景类型" prop="bgType">
<el-radio-group v-model="formData.bgType"> <el-radio-group v-model="formData.bgType">
<el-radio label="color">纯色</el-radio> <el-radio value="color">纯色</el-radio>
<el-radio label="img">图片</el-radio> <el-radio value="img">图片</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="背景颜色" prop="bgColor" v-if="formData.bgType === 'color'"> <el-form-item label="背景颜色" prop="bgColor" v-if="formData.bgType === 'color'">

View File

@ -11,10 +11,10 @@
<el-form-item label="显示次数" :prop="`list[${index}].showType`"> <el-form-item label="显示次数" :prop="`list[${index}].showType`">
<el-radio-group v-model="element.showType"> <el-radio-group v-model="element.showType">
<el-tooltip content="只显示一次,下次打开时不显示" placement="bottom"> <el-tooltip content="只显示一次,下次打开时不显示" placement="bottom">
<el-radio label="once">一次</el-radio> <el-radio value="once">一次</el-radio>
</el-tooltip> </el-tooltip>
<el-tooltip content="每次打开时都会显示" placement="bottom"> <el-tooltip content="每次打开时都会显示" placement="bottom">
<el-radio label="always">不限</el-radio> <el-radio value="always">不限</el-radio>
</el-tooltip> </el-tooltip>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>

View File

@ -8,17 +8,17 @@
<el-form-item label="布局" prop="type"> <el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType"> <el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列大图" placement="bottom"> <el-tooltip class="item" content="单列大图" placement="bottom">
<el-radio-button label="oneColBigImg"> <el-radio-button value="oneColBigImg">
<Icon icon="fluent:text-column-one-24-filled" /> <Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="单列小图" placement="bottom"> <el-tooltip class="item" content="单列小图" placement="bottom">
<el-radio-button label="oneColSmallImg"> <el-radio-button value="oneColSmallImg">
<Icon icon="fluent:text-column-two-left-24-filled" /> <Icon icon="fluent:text-column-two-left-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="双列" placement="bottom"> <el-tooltip class="item" content="双列" placement="bottom">
<el-radio-button label="twoCol"> <el-radio-button value="twoCol">
<Icon icon="fluent:text-column-two-24-filled" /> <Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
@ -74,8 +74,8 @@
<el-card header="按钮" class="property-group" shadow="never"> <el-card header="按钮" class="property-group" shadow="never">
<el-form-item label="按钮类型" prop="btnBuy.type"> <el-form-item label="按钮类型" prop="btnBuy.type">
<el-radio-group v-model="formData.btnBuy.type"> <el-radio-group v-model="formData.btnBuy.type">
<el-radio-button label="text">文字</el-radio-button> <el-radio-button value="text">文字</el-radio-button>
<el-radio-button label="img">图片</el-radio-button> <el-radio-button value="img">图片</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<template v-if="formData.btnBuy.type === 'text'"> <template v-if="formData.btnBuy.type === 'text'">

View File

@ -8,17 +8,17 @@
<el-form-item label="布局" prop="type"> <el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType"> <el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="双列" placement="bottom"> <el-tooltip class="item" content="双列" placement="bottom">
<el-radio-button label="twoCol"> <el-radio-button value="twoCol">
<Icon icon="fluent:text-column-two-24-filled" /> <Icon icon="fluent:text-column-two-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="三列" placement="bottom"> <el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button label="threeCol"> <el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" /> <Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="水平滑动" placement="bottom"> <el-tooltip class="item" content="水平滑动" placement="bottom">
<el-radio-button label="horizSwiper"> <el-radio-button value="horizSwiper">
<Icon icon="system-uicons:carousel" /> <Icon icon="system-uicons:carousel" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>

View File

@ -54,7 +54,7 @@
class="text-12px" class="text-12px"
:style="{ color: property.fields.price.color }" :style="{ color: property.fields.price.color }"
> >
{{ spu.price }} {{ fenToYuan(spu.combinationPrice || spu.price || 0) }}
</span> </span>
</div> </div>
</div> </div>
@ -66,6 +66,9 @@
import { PromotionCombinationProperty } from './config' import { PromotionCombinationProperty } from './config'
import * as ProductSpuApi from '@/api/mall/product/spu' import * as ProductSpuApi from '@/api/mall/product/spu'
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity' import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
import { Spu } from '@/api/mall/product/spu'
import { CombinationProductVO } from '@/api/mall/promotion/combination/combinationActivity'
import { fenToYuan } from '@/utils'
/** 拼团 */ /** 拼团 */
defineOptions({ name: 'PromotionCombination' }) defineOptions({ name: 'PromotionCombination' })
@ -80,6 +83,13 @@ watch(
const activity = await CombinationActivityApi.getCombinationActivity(props.property.activityId) const activity = await CombinationActivityApi.getCombinationActivity(props.property.activityId)
if (!activity?.spuId) return if (!activity?.spuId) return
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)] spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
//
activity.products.forEach((product: CombinationProductVO) => {
spuList.value.forEach((spu: Spu) => {
// 便
spu.combinationPrice = Math.min(spu.combinationPrice || Infinity, product.combinationPrice) // SPU
})
})
}, },
{ {
immediate: true, immediate: true,

View File

@ -17,12 +17,12 @@
<el-form-item label="布局" prop="type"> <el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType"> <el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列" placement="bottom"> <el-tooltip class="item" content="单列" placement="bottom">
<el-radio-button label="oneCol"> <el-radio-button value="oneCol">
<Icon icon="fluent:text-column-one-24-filled" /> <Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="三列" placement="bottom"> <el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button label="threeCol"> <el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" /> <Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>

View File

@ -54,7 +54,7 @@
class="text-12px" class="text-12px"
:style="{ color: property.fields.price.color }" :style="{ color: property.fields.price.color }"
> >
{{ spu.price }} {{ fenToYuan(spu.seckillPrice || spu.price || 0) }}
</span> </span>
</div> </div>
</div> </div>
@ -66,6 +66,9 @@
import { PromotionSeckillProperty } from './config' import { PromotionSeckillProperty } from './config'
import * as ProductSpuApi from '@/api/mall/product/spu' import * as ProductSpuApi from '@/api/mall/product/spu'
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity' import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
import { Spu } from '@/api/mall/product/spu'
import { SeckillProductVO } from '@/api/mall/promotion/seckill/seckillActivity'
import { fenToYuan } from '@/utils'
/** 秒杀 */ /** 秒杀 */
defineOptions({ name: 'PromotionSeckill' }) defineOptions({ name: 'PromotionSeckill' })
@ -80,6 +83,13 @@ watch(
const activity = await SeckillActivityApi.getSeckillActivity(props.property.activityId) const activity = await SeckillActivityApi.getSeckillActivity(props.property.activityId)
if (!activity?.spuId) return if (!activity?.spuId) return
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)] spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
//
activity.products.forEach((product: SeckillProductVO) => {
spuList.value.forEach((spu: Spu) => {
spu.seckillPrice = Math.min(spu.seckillPrice || Infinity, product.seckillPrice) // SPU
})
})
}, },
{ {
immediate: true, immediate: true,

View File

@ -17,12 +17,12 @@
<el-form-item label="布局" prop="type"> <el-form-item label="布局" prop="type">
<el-radio-group v-model="formData.layoutType"> <el-radio-group v-model="formData.layoutType">
<el-tooltip class="item" content="单列" placement="bottom"> <el-tooltip class="item" content="单列" placement="bottom">
<el-radio-button label="oneCol"> <el-radio-button value="oneCol">
<Icon icon="fluent:text-column-one-24-filled" /> <Icon icon="fluent:text-column-one-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip class="item" content="三列" placement="bottom"> <el-tooltip class="item" content="三列" placement="bottom">
<el-radio-button label="threeCol"> <el-radio-button value="threeCol">
<Icon icon="fluent:text-column-three-24-filled" /> <Icon icon="fluent:text-column-three-24-filled" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>

View File

@ -13,12 +13,12 @@
<el-form-item label="框体样式"> <el-form-item label="框体样式">
<el-radio-group v-model="formData!.borderRadius"> <el-radio-group v-model="formData!.borderRadius">
<el-tooltip content="方形" placement="top"> <el-tooltip content="方形" placement="top">
<el-radio-button :label="0"> <el-radio-button :value="0">
<Icon icon="tabler:input-search" /> <Icon icon="tabler:input-search" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="圆形" placement="top"> <el-tooltip content="圆形" placement="top">
<el-radio-button :label="10"> <el-radio-button :value="10">
<Icon icon="iconoir:input-search" /> <Icon icon="iconoir:input-search" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
@ -30,12 +30,12 @@
<el-form-item label="文本位置" prop="placeholderPosition"> <el-form-item label="文本位置" prop="placeholderPosition">
<el-radio-group v-model="formData!.placeholderPosition"> <el-radio-group v-model="formData!.placeholderPosition">
<el-tooltip content="居左" placement="top"> <el-tooltip content="居左" placement="top">
<el-radio-button label="left"> <el-radio-button value="left">
<Icon icon="ant-design:align-left-outlined" /> <Icon icon="ant-design:align-left-outlined" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="居中" placement="top"> <el-tooltip content="居中" placement="top">
<el-radio-button label="center"> <el-radio-button value="center">
<Icon icon="ant-design:align-center-outlined" /> <Icon icon="ant-design:align-center-outlined" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>

View File

@ -27,8 +27,8 @@
</el-form-item> </el-form-item>
<el-form-item label="导航背景"> <el-form-item label="导航背景">
<el-radio-group v-model="formData!.style.bgType"> <el-radio-group v-model="formData!.style.bgType">
<el-radio-button label="color">纯色</el-radio-button> <el-radio-button value="color">纯色</el-radio-button>
<el-radio-button label="img">图片</el-radio-button> <el-radio-button value="img">图片</el-radio-button>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="选择颜色" v-if="formData!.style.bgType === 'color'"> <el-form-item label="选择颜色" v-if="formData!.style.bgType === 'color'">

View File

@ -10,12 +10,12 @@
<el-form-item label="标题位置" prop="textAlign"> <el-form-item label="标题位置" prop="textAlign">
<el-radio-group v-model="formData!.textAlign"> <el-radio-group v-model="formData!.textAlign">
<el-tooltip content="居左" placement="top"> <el-tooltip content="居左" placement="top">
<el-radio-button label="left"> <el-radio-button value="left">
<Icon icon="ant-design:align-left-outlined" /> <Icon icon="ant-design:align-left-outlined" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
<el-tooltip content="居中" placement="top"> <el-tooltip content="居中" placement="top">
<el-radio-button label="center"> <el-radio-button value="center">
<Icon icon="ant-design:align-center-outlined" /> <Icon icon="ant-design:align-center-outlined" />
</el-radio-button> </el-radio-button>
</el-tooltip> </el-tooltip>
@ -88,9 +88,9 @@
<template v-if="formData.more.show"> <template v-if="formData.more.show">
<el-form-item label="样式" prop="more.type"> <el-form-item label="样式" prop="more.type">
<el-radio-group v-model="formData.more.type"> <el-radio-group v-model="formData.more.type">
<el-radio label="text">文字</el-radio> <el-radio value="text">文字</el-radio>
<el-radio label="icon">图标</el-radio> <el-radio value="icon">图标</el-radio>
<el-radio label="all">文字+图标</el-radio> <el-radio value="all">文字+图标</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="更多文字" prop="more.text" v-show="formData.more.type !== 'icon'"> <el-form-item label="更多文字" prop="more.text" v-show="formData.more.type !== 'icon'">

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<el-radio-group v-model="shortcutDays" @change="handleShortcutDaysChange"> <el-radio-group v-model="shortcutDays" @change="handleShortcutDaysChange">
<el-radio-button :label="1">昨天</el-radio-button> <el-radio-button :value="1">昨天</el-radio-button>
<el-radio-button :label="7">最近7天</el-radio-button> <el-radio-button :value="7">最近7天</el-radio-button>
<el-radio-button :label="30">最近30天</el-radio-button> <el-radio-button :value="30">最近30天</el-radio-button>
</el-radio-group> </el-radio-group>
<el-date-picker <el-date-picker
v-model="times" v-model="times"

View File

@ -45,17 +45,20 @@
<el-checkbox <el-checkbox
v-model="loopInstanceForm.asyncBefore" v-model="loopInstanceForm.asyncBefore"
label="异步前" label="异步前"
value="异步前"
@change="updateLoopAsync('asyncBefore')" @change="updateLoopAsync('asyncBefore')"
/> />
<el-checkbox <el-checkbox
v-model="loopInstanceForm.asyncAfter" v-model="loopInstanceForm.asyncAfter"
label="异步后" label="异步后"
value="异步后"
@change="updateLoopAsync('asyncAfter')" @change="updateLoopAsync('asyncAfter')"
/> />
<el-checkbox <el-checkbox
v-model="loopInstanceForm.exclusive" v-model="loopInstanceForm.exclusive"
v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore" v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore"
label="排除" label="排除"
value="排除"
@change="updateLoopAsync('exclusive')" @change="updateLoopAsync('exclusive')"
/> />
</el-form-item> </el-form-item>

View File

@ -6,13 +6,20 @@
<el-checkbox <el-checkbox
v-model="taskConfigForm.asyncBefore" v-model="taskConfigForm.asyncBefore"
label="异步前" label="异步前"
value="异步前"
@change="changeTaskAsync"
/>
<el-checkbox
v-model="taskConfigForm.asyncAfter"
label="异步后"
value="异步后"
@change="changeTaskAsync" @change="changeTaskAsync"
/> />
<el-checkbox v-model="taskConfigForm.asyncAfter" label="异步后" @change="changeTaskAsync" />
<el-checkbox <el-checkbox
v-model="taskConfigForm.exclusive" v-model="taskConfigForm.exclusive"
v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore" v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore"
label="排除" label="排除"
value="排除"
@change="changeTaskAsync" @change="changeTaskAsync"
/> />
</el-form-item> </el-form-item>

View File

@ -90,6 +90,11 @@ export default defineComponent({
backgroundColor="var(--left-menu-bg-color)" backgroundColor="var(--left-menu-bg-color)"
textColor="var(--left-menu-text-color)" textColor="var(--left-menu-text-color)"
activeTextColor="var(--left-menu-text-active-color)" activeTextColor="var(--left-menu-text-active-color)"
popperClass={
unref(menuMode) === 'vertical'
? `${prefixCls}-popper--vertical`
: `${prefixCls}-popper--horizontal`
}
onSelect={menuSelect} onSelect={menuSelect}
> >
{{ {{

View File

@ -126,6 +126,8 @@ const copyConfig = async () => {
message: ${appStore.getMessage}, message: ${appStore.getMessage},
// //
tagsView: ${appStore.getTagsView}, tagsView: ${appStore.getTagsView},
//
tagsViewImmerse: ${appStore.getTagsViewImmerse},
// //
getTagsViewIcon: ${appStore.getTagsViewIcon}, getTagsViewIcon: ${appStore.getTagsViewIcon},
// logo // logo

View File

@ -73,6 +73,13 @@ const tagsViewChange = (show: boolean) => {
appStore.setTagsView(show) appStore.setTagsView(show)
} }
//
const tagsViewImmerse = ref(appStore.getTagsViewImmerse)
const tagsViewImmerseChange = (immerse: boolean) => {
appStore.setTagsViewImmerse(immerse)
}
// //
const tagsViewIcon = ref(appStore.getTagsViewIcon) const tagsViewIcon = ref(appStore.getTagsViewIcon)
@ -181,6 +188,11 @@ watch(
<ElSwitch v-model="tagsView" @change="tagsViewChange" /> <ElSwitch v-model="tagsView" @change="tagsViewChange" />
</div> </div>
<div class="flex items-center justify-between">
<span class="text-14px">{{ t('setting.tagsViewImmerse') }}</span>
<ElSwitch v-model="tagsViewImmerse" @change="tagsViewImmerseChange" />
</div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<span class="text-14px">{{ t('setting.tagsViewIcon') }}</span> <span class="text-14px">{{ t('setting.tagsViewIcon') }}</span>
<ElSwitch v-model="tagsViewIcon" @change="tagsViewIconChange" /> <ElSwitch v-model="tagsViewIcon" @change="tagsViewIconChange" />

View File

@ -33,6 +33,8 @@ const affixTagArr = ref<RouteLocationNormalizedLoaded[]>([])
const appStore = useAppStore() const appStore = useAppStore()
const tagsViewImmerse = computed(() => appStore.getTagsViewImmerse)
const tagsViewIcon = computed(() => appStore.getTagsViewIcon) const tagsViewIcon = computed(() => appStore.getTagsViewIcon)
const isDark = computed(() => appStore.getIsDark) const isDark = computed(() => appStore.getIsDark)
@ -266,7 +268,7 @@ watch(
class="relative w-full flex bg-[#fff] dark:bg-[var(--el-bg-color)]" class="relative w-full flex bg-[#fff] dark:bg-[var(--el-bg-color)]"
> >
<span <span
:class="`${prefixCls}__tool ${prefixCls}__tool--first`" :class="tagsViewImmerse ? '' : `${prefixCls}__tool ${prefixCls}__tool--first`"
class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center" class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
@click="move(-200)" @click="move(-200)"
> >
@ -343,6 +345,9 @@ watch(
:tag-item="item" :tag-item="item"
:class="[ :class="[
`${prefixCls}__item`, `${prefixCls}__item`,
tagsViewImmerse ? `${prefixCls}__item--immerse` : '',
tagsViewIcon ? `${prefixCls}__item--icon` : '',
tagsViewImmerse && tagsViewIcon ? `${prefixCls}__item--immerse--icon` : '',
item?.meta?.affix ? `${prefixCls}__item--affix` : '', item?.meta?.affix ? `${prefixCls}__item--affix` : '',
{ {
'is-active': isActive(item) 'is-active': isActive(item)
@ -354,16 +359,17 @@ watch(
<router-link :ref="tagLinksRefs.set" :to="{ ...item }" custom v-slot="{ navigate }"> <router-link :ref="tagLinksRefs.set" :to="{ ...item }" custom v-slot="{ navigate }">
<div <div
@click="navigate" @click="navigate"
class="h-full flex items-center justify-center whitespace-nowrap pl-15px" :class="`h-full flex items-center justify-center whitespace-nowrap pl-15px ${prefixCls}__item--label`"
> >
<Icon <Icon
v-if=" v-if="
item?.matched && tagsViewIcon &&
item?.matched[1] && (item?.meta?.icon ||
item?.matched[1]?.meta?.icon && (item?.matched &&
tagsViewIcon item.matched[0] &&
item.matched[item.matched.length - 1].meta?.icon))
" "
:icon="item?.matched[1]?.meta?.icon" :icon="item?.meta?.icon || item.matched[item.matched.length - 1].meta.icon"
:size="12" :size="12"
class="mr-5px" class="mr-5px"
/> />
@ -383,7 +389,7 @@ watch(
</ElScrollbar> </ElScrollbar>
</div> </div>
<span <span
:class="`${prefixCls}__tool`" :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center" class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
@click="move(200)" @click="move(200)"
> >
@ -394,7 +400,7 @@ watch(
/> />
</span> </span>
<span <span
:class="`${prefixCls}__tool`" :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center" class="h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
@click="refreshSelectedTag(selectedTag)" @click="refreshSelectedTag(selectedTag)"
> >
@ -459,7 +465,7 @@ watch(
]" ]"
> >
<span <span
:class="`${prefixCls}__tool`" :class="tagsViewImmerse ? '' : `${prefixCls}__tool`"
class="block h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center" class="block h-[var(--tags-view-height)] w-[var(--tags-view-height)] flex cursor-pointer items-center justify-center"
> >
<Icon <Icon
@ -511,7 +517,7 @@ $prefix-cls: #{$namespace}-tags-view;
position: relative; position: relative;
top: 2px; top: 2px;
height: calc(100% - 6px); height: calc(100% - 6px);
padding-right: 25px; padding-right: 15px;
margin-left: 4px; margin-left: 4px;
font-size: 12px; font-size: 12px;
cursor: pointer; cursor: pointer;
@ -532,6 +538,10 @@ $prefix-cls: #{$namespace}-tags-view;
} }
} }
&__item--icon {
padding-right: 20px;
}
&__item:not(.is-active) { &__item:not(.is-active) {
&:hover { &:hover {
color: var(--el-color-primary); color: var(--el-color-primary);
@ -548,6 +558,37 @@ $prefix-cls: #{$namespace}-tags-view;
} }
} }
} }
&__item--immerse {
top: 3px;
padding-right: 35px;
margin: 0 -10px;
border: 1px solid transparent;
-webkit-mask-box-image: url("data:image/svg+xml,%3Csvg width='68' height='34' viewBox='0 0 68 34' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m27,0c-7.99582,0 -11.95105,0.00205 -12,12l0,6c0,8.284 -0.48549,16.49691 -8.76949,16.49691l54.37857,-0.11145c-8.284,0 -8.60908,-8.10146 -8.60908,-16.38546l0,-6c0.11145,-12.08445 -4.38441,-12 -12,-12l-13,0z' fill='%23409eff'/%3E%3C/svg%3E")
12 27 15;
.#{$prefix-cls}__item--label {
padding-left: 35px;
}
.#{$prefix-cls}__item--close {
right: 20px;
}
}
&__item--immerse--icon {
padding-right: 35px;
}
&__item--immerse:not(.is-active) {
&:hover {
color: var(--el-color-white);
background-color: var(--el-color-primary);
.#{$prefix-cls}__item--close {
:deep(span) {
color: var(--el-color-white) !important;
}
}
}
}
} }
.dark { .dark {

View File

@ -92,6 +92,7 @@ export default {
localeIcon: '多语言图标', localeIcon: '多语言图标',
messageIcon: '消息图标', messageIcon: '消息图标',
tagsView: '标签页', tagsView: '标签页',
tagsViewImmerse: '标签页沉浸',
logo: '标志', logo: '标志',
greyMode: '灰色模式', greyMode: '灰色模式',
fixedHeader: '固定头部', fixedHeader: '固定头部',

View File

@ -589,11 +589,20 @@ const remainingRouter: AppRouteRecordRaw[] = [
meta: { meta: {
title: '绘图作品', title: '绘图作品',
icon: 'ep:home-filled', icon: 'ep:home-filled',
noCache: false, noCache: false
affix: true
} }
} }
] ]
},
{
path: '/:pathMatch(.*)*',
component: () => import('@/views/Error/404.vue'),
name: '',
meta: {
title: '404',
hidden: true,
breadcrumb: false
}
} }
] ]

View File

@ -21,6 +21,7 @@ interface AppState {
locale: boolean locale: boolean
message: boolean message: boolean
tagsView: boolean tagsView: boolean
tagsViewImmerse: boolean
tagsViewIcon: boolean tagsViewIcon: boolean
logo: boolean logo: boolean
fixedHeader: boolean fixedHeader: boolean
@ -58,6 +59,7 @@ export const useAppStore = defineStore('app', {
locale: true, // 多语言图标 locale: true, // 多语言图标
message: true, // 消息图标 message: true, // 消息图标
tagsView: true, // 标签页 tagsView: true, // 标签页
tagsViewImmerse: false, // 标签页沉浸
tagsViewIcon: true, // 是否显示标签图标 tagsViewIcon: true, // 是否显示标签图标
logo: true, // logo logo: true, // logo
fixedHeader: true, // 固定toolheader fixedHeader: true, // 固定toolheader
@ -131,6 +133,9 @@ export const useAppStore = defineStore('app', {
getTagsView(): boolean { getTagsView(): boolean {
return this.tagsView return this.tagsView
}, },
getTagsViewImmerse(): boolean {
return this.tagsViewImmerse
},
getTagsViewIcon(): boolean { getTagsViewIcon(): boolean {
return this.tagsViewIcon return this.tagsViewIcon
}, },
@ -208,6 +213,9 @@ export const useAppStore = defineStore('app', {
setTagsView(tagsView: boolean) { setTagsView(tagsView: boolean) {
this.tagsView = tagsView this.tagsView = tagsView
}, },
setTagsViewImmerse(tagsViewImmerse: boolean) {
this.tagsViewImmerse = tagsViewImmerse
},
setTagsViewIcon(tagsViewIcon: boolean) { setTagsViewIcon(tagsViewIcon: boolean) {
this.tagsViewIcon = tagsViewIcon this.tagsViewIcon = tagsViewIcon
}, },

View File

@ -40,10 +40,12 @@ export const usePermissionStore = defineStore('permission', {
} }
const routerMap: AppRouteRecordRaw[] = generateRoute(res) const routerMap: AppRouteRecordRaw[] = generateRoute(res)
// 动态路由404一定要放到最后面 // 动态路由404一定要放到最后面
// preschoolervue-router@4以后已支持静态404路由此处可不再追加
this.addRouters = routerMap.concat([ this.addRouters = routerMap.concat([
{ {
path: '/:path(.*)*', path: '/:path(.*)*',
redirect: '/404', // redirect: '/404',
component: () => import('@/views/Error/404.vue'),
name: '404Page', name: '404Page',
meta: { meta: {
hidden: true, hidden: true,

View File

@ -1,5 +1,6 @@
@import './var.css'; @import './var.css';
@import './FormCreate/index.scss'; @import './FormCreate/index.scss';
@import './theme.scss';
@import 'element-plus/theme-chalk/dark/css-vars.css'; @import 'element-plus/theme-chalk/dark/css-vars.css';
.reset-margin [class*='el-icon'] + span { .reset-margin [class*='el-icon'] + span {

View File

@ -34,16 +34,31 @@ const download = {
download0(data, fileName, 'text/markdown') download0(data, fileName, 'text/markdown')
}, },
// 下载图片(允许跨域) // 下载图片(允许跨域)
image: (url: string) => { image: ({
url,
canvasWidth,
canvasHeight,
drawWithImageSize = true
}: {
url: string
canvasWidth?: number // 指定画布宽度
canvasHeight?: number // 指定画布高度
drawWithImageSize?: boolean // 将图片绘制在画布上时带上图片的宽高值, 默认是要带上的
}) => {
const image = new Image() const image = new Image()
image.setAttribute('crossOrigin', 'anonymous') // image.setAttribute('crossOrigin', 'anonymous')
image.src = url image.src = url
image.onload = () => { image.onload = () => {
const canvas = document.createElement('canvas') const canvas = document.createElement('canvas')
canvas.width = image.width canvas.width = canvasWidth || image.width
canvas.height = image.height canvas.height = canvasHeight || image.height
const ctx = canvas.getContext('2d') as CanvasDrawImage const ctx = canvas.getContext('2d') as CanvasRenderingContext2D
ctx.drawImage(image, 0, 0, image.width, image.height) ctx?.clearRect(0, 0, canvas.width, canvas.height)
if (drawWithImageSize) {
ctx.drawImage(image, 0, 0, image.width, image.height)
} else {
ctx.drawImage(image, 0, 0)
}
const url = canvas.toDataURL('image/png') const url = canvas.toDataURL('image/png')
const a = document.createElement('a') const a = document.createElement('a')
a.href = url a.href = url

View File

@ -66,10 +66,10 @@ export const defaultShortcuts = [
export function formatDate(date: Date, format?: string): string { export function formatDate(date: Date, format?: string): string {
// 日期不存在,则返回空 // 日期不存在,则返回空
if (!date) { if (!date) {
return '' return '';
} }
// 日期存在,则进行格式化 // 日期存在,则进行格式化
return date ? dayjs(date).format(format ?? 'YYYY-MM-DD HH:mm:ss') : '' return date ? dayjs(date).format(format ?? 'YYYY-MM-DD HH:mm:ss') : '';
} }
/** /**

View File

@ -5,7 +5,7 @@
> >
<div class="relative mx-auto h-full flex"> <div class="relative mx-auto h-full flex">
<div <div
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden`" :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
> >
<!-- 左上角的 logo + 系统标题 --> <!-- 左上角的 logo + 系统标题 -->
<div class="relative flex items-center text-white"> <div class="relative flex items-center text-white">
@ -27,7 +27,9 @@
</TransitionGroup> </TransitionGroup>
</div> </div>
</div> </div>
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px"> <div
class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
>
<!-- 右上角的主题语言选择 --> <!-- 右上角的主题语言选择 -->
<div <div
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end" class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
@ -36,7 +38,7 @@
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" /> <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
</div> </div>
<div class="flex items-center justify-end space-x-10px"> <div class="flex items-center justify-end space-x-10px h-48px">
<ThemeSwitch /> <ThemeSwitch />
<LocaleDropdown class="dark:text-white lt-xl:text-white" /> <LocaleDropdown class="dark:text-white lt-xl:text-white" />
</div> </div>
@ -44,7 +46,7 @@
<!-- 右边的登录界面 --> <!-- 右边的登录界面 -->
<Transition appear enter-active-class="animate__animated animate__bounceInRight"> <Transition appear enter-active-class="animate__animated animate__bounceInRight">
<div <div
class="m-auto h-full w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px" class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
> >
<!-- 账号登录 --> <!-- 账号登录 -->
<LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" /> <LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
@ -102,3 +104,15 @@ $prefix-cls: #{$namespace}-login;
} }
} }
</style> </style>
<style lang="scss">
.dark .login-form {
.el-divider__text {
background-color: var(--login-bg-color);
}
.el-card {
background-color: var(--login-bg-color);
}
}
</style>

View File

@ -1,11 +1,11 @@
<template> <template>
<div <div
:class="prefixCls" :class="prefixCls"
class="relative h-[100%] lt-xl:bg-[var(--login-bg-color)] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px" class="relative h-[100%] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px lt-xl:px-10px"
> >
<div class="relative mx-auto h-full flex"> <div class="relative mx-auto h-full flex">
<div <div
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden`" :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
> >
<!-- 左上角的 logo + 系统标题 --> <!-- 左上角的 logo + 系统标题 -->
<div class="relative flex items-center text-white"> <div class="relative flex items-center text-white">
@ -27,7 +27,9 @@
</TransitionGroup> </TransitionGroup>
</div> </div>
</div> </div>
<div class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px"> <div
class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
>
<!-- 右上角的主题语言选择 --> <!-- 右上角的主题语言选择 -->
<div <div
class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end" class="flex items-center justify-between text-white at-2xl:justify-end at-xl:justify-end"
@ -36,7 +38,7 @@
<img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" /> <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
<span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span> <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
</div> </div>
<div class="flex items-center justify-end space-x-10px"> <div class="flex items-center justify-end space-x-10px h-48px">
<ThemeSwitch /> <ThemeSwitch />
<LocaleDropdown class="dark:text-white lt-xl:text-white" /> <LocaleDropdown class="dark:text-white lt-xl:text-white" />
</div> </div>
@ -44,7 +46,7 @@
<!-- 右边的登录界面 --> <!-- 右边的登录界面 -->
<Transition appear enter-active-class="animate__animated animate__bounceInRight"> <Transition appear enter-active-class="animate__animated animate__bounceInRight">
<div <div
class="m-auto h-full w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px" class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
> >
<!-- 账号登录 --> <!-- 账号登录 -->
<el-form <el-form
@ -274,10 +276,11 @@ const handleLogin = async (params) => {
const code = route?.query?.code as string const code = route?.query?.code as string
const state = route?.query?.state as string const state = route?.query?.state as string
const loginDataLoginForm = { ...loginData.loginForm }
const res = await LoginApi.login({ const res = await LoginApi.login({
// //
username: loginData.loginForm.username, username: loginDataLoginForm.username,
password: loginData.loginForm.password, password: loginDataLoginForm.password,
captchaVerification: params.captchaVerification, captchaVerification: params.captchaVerification,
// //
socialCode: code, socialCode: code,
@ -292,8 +295,8 @@ const handleLogin = async (params) => {
text: '正在加载系统中...', text: '正在加载系统中...',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}) })
if (loginData.loginForm.rememberMe) { if (loginDataLoginForm.rememberMe) {
authUtil.setLoginForm(loginData.loginForm) authUtil.setLoginForm(loginDataLoginForm)
} else { } else {
authUtil.removeLoginForm() authUtil.removeLoginForm()
} }

View File

@ -249,8 +249,9 @@ const handleLogin = async (params) => {
if (!data) { if (!data) {
return return
} }
loginData.loginForm.captchaVerification = params.captchaVerification const loginDataLoginForm = { ...loginData.loginForm }
const res = await LoginApi.login(loginData.loginForm) loginDataLoginForm.captchaVerification = params.captchaVerification
const res = await LoginApi.login(loginDataLoginForm)
if (!res) { if (!res) {
return return
} }
@ -259,8 +260,8 @@ const handleLogin = async (params) => {
text: '正在加载系统中...', text: '正在加载系统中...',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}) })
if (loginData.loginForm.rememberMe) { if (loginDataLoginForm.rememberMe) {
authUtil.setLoginForm(loginData.loginForm) authUtil.setLoginForm(loginDataLoginForm)
} else { } else {
authUtil.removeLoginForm() authUtil.removeLoginForm()
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<el-row v-show="getShow" style="margin-right: -10px; margin-left: -10px"> <el-row class="login-form" v-show="getShow" style="margin-right: -10px; margin-left: -10px">
<el-col :span="24" style="padding-right: 10px; padding-left: 10px"> <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
<LoginFormTitle style="width: 100%" /> <LoginFormTitle style="width: 100%" />
</el-col> </el-col>

View File

@ -3,7 +3,7 @@
v-show="getShow" v-show="getShow"
:rules="rules" :rules="rules"
:schema="schema" :schema="schema"
class="dark:(border-1 border-[var(--el-border-color)] border-solid)" class="w-[100%] dark:(border-1 border-[var(--el-border-color)] border-solid)"
hide-required-asterisk hide-required-asterisk
label-position="top" label-position="top"
size="large" size="large"

View File

@ -14,7 +14,7 @@
<el-checkbox <el-checkbox
v-for="scope in queryParams.scopes" v-for="scope in queryParams.scopes"
:key="scope" :key="scope"
:label="scope" :value="scope"
style="display: block; margin-bottom: -10px" style="display: block; margin-bottom: -10px"
> >
{{ formatScope(scope) }} {{ formatScope(scope) }}

View File

@ -2,8 +2,8 @@
<Form ref="formRef" :labelWidth="200" :rules="rules" :schema="schema"> <Form ref="formRef" :labelWidth="200" :rules="rules" :schema="schema">
<template #sex="form"> <template #sex="form">
<el-radio-group v-model="form['sex']"> <el-radio-group v-model="form['sex']">
<el-radio :label="1">{{ t('profile.user.man') }}</el-radio> <el-radio :value="1">{{ t('profile.user.man') }}</el-radio>
<el-radio :label="2">{{ t('profile.user.woman') }}</el-radio> <el-radio :value="2">{{ t('profile.user.woman') }}</el-radio>
</el-radio-group> </el-radio-group>
</template> </template>
</Form> </Form>
@ -27,7 +27,7 @@ defineOptions({ name: 'BasicInfo' })
const { t } = useI18n() const { t } = useI18n()
const message = useMessage() // const message = useMessage() //
const userStore = useUserStore() const userStore = useUserStore()
// //
const rules = reactive<FormRules>({ const rules = reactive<FormRules>({
nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }], nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }],

View File

@ -150,7 +150,7 @@ const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => {
} }
// //
if (type === 'download') { if (type === 'download') {
await download.image(imageDetail.picUrl) await download.image({ url: imageDetail.picUrl })
return return
} }
// //

View File

@ -19,7 +19,7 @@
<div class="flex flex-col items-center justify-center" v-html="html"></div> <div class="flex flex-col items-center justify-center" v-html="html"></div>
</div> </div>
<div ref="mindmapRef" class="wh-full"> <div ref="mindMapRef" class="wh-full">
<svg ref="svgRef" class="w-full" :style="{ height: `${contentAreaHeight}px` }" /> <svg ref="svgRef" class="w-full" :style="{ height: `${contentAreaHeight}px` }" />
<div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div> <div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
</div> </div>
@ -32,20 +32,20 @@ import { Markmap } from 'markmap-view'
import { Transformer } from 'markmap-lib' import { Transformer } from 'markmap-lib'
import { Toolbar } from 'markmap-toolbar' import { Toolbar } from 'markmap-toolbar'
import markdownit from 'markdown-it' import markdownit from 'markdown-it'
import download from '@/utils/download'
const md = markdownit() const md = markdownit()
const message = useMessage() // const message = useMessage() //
// TODO @hheromindmap mindMap
const props = defineProps<{ const props = defineProps<{
mindmapResult: string // TODO @hhero generatedContent generatedContent: string //
isEnd: boolean // isEnd: boolean //
isGenerating: boolean // isGenerating: boolean //
isStart: boolean // html isStart: boolean // html
}>() }>()
const contentRef = ref<HTMLDivElement>() // header const contentRef = ref<HTMLDivElement>() // header
const mdContainerRef = ref<HTMLDivElement>() // markdown const mdContainerRef = ref<HTMLDivElement>() // markdown
const mindmapRef = ref<HTMLDivElement>() // const mindMapRef = ref<HTMLDivElement>() //
const svgRef = ref<SVGElement>() // svg const svgRef = ref<SVGElement>() // svg
const toolBarRef = ref<HTMLDivElement>() // const toolBarRef = ref<HTMLDivElement>() //
const html = ref('') // const html = ref('') //
@ -66,15 +66,16 @@ onMounted(() => {
} }
}) })
watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => { watch(props, ({ generatedContent, isGenerating, isEnd, isStart }) => {
// markdown // markdown
if (isStart) { if (isStart) {
html.value = '' html.value = ''
} }
// 使 markdown // 使 markdown
if (isGenerating) { if (isGenerating) {
html.value = md.render(mindmapResult) html.value = md.render(generatedContent)
} }
//
if (isEnd) { if (isEnd) {
update() update()
} }
@ -83,7 +84,7 @@ watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
/** 更新思维导图的展示 */ /** 更新思维导图的展示 */
const update = () => { const update = () => {
try { try {
const { root } = transformer.transform(processContent(props.mindmapResult)) const { root } = transformer.transform(processContent(props.generatedContent))
markMap?.setData(root) markMap?.setData(root)
markMap?.fit() markMap?.fit()
} catch (e) { } catch (e) {
@ -105,32 +106,19 @@ const processContent = (text: string) => {
return arr.join('\n') return arr.join('\n')
} }
/** 下载图片 */ /** 下载图片download SVG to png file */
// TODO @hhhero download src/utils/download.ts image
// download SVG to png file
const downloadImage = () => { const downloadImage = () => {
const svgElement = mindmapRef.value const svgElement = mindMapRef.value
// SVG // SVG
const serializer = new XMLSerializer() const serializer = new XMLSerializer()
const source = const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(svgRef.value!)}`
'<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(svgRef.value!) const base64Url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}`
const image = new Image() download.image({
image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source) url: base64Url,
canvasWidth: svgElement?.offsetWidth,
// canvasHeight: svgElement?.offsetHeight,
const canvas = document.createElement('canvas') drawWithImageSize: false
canvas.width = svgElement?.offsetWidth || 0 })
canvas.height = svgElement?.offsetHeight || 0
let context = canvas.getContext('2d')
context?.clearRect(0, 0, canvas.width, canvas.height)
image.onload = function () {
context?.drawImage(image, 0, 0)
const a = document.createElement('a')
a.download = 'mindmap.png'
a.href = canvas.toDataURL(`image/png`)
a.click()
}
} }
defineExpose({ defineExpose({

View File

@ -10,7 +10,7 @@
<!--右边生成思维导图区域--> <!--右边生成思维导图区域-->
<Right <Right
ref="rightRef" ref="rightRef"
:mindmapResult="mindmapResult" :generatedContent="generatedContent"
:isEnd="isEnd" :isEnd="isEnd"
:isGenerating="isGenerating" :isGenerating="isGenerating"
:isStart="isStart" :isStart="isStart"
@ -33,15 +33,15 @@ const isStart = ref(false) // 开始生成,用来清空思维导图
const isEnd = ref(true) // const isEnd = ref(true) //
const message = useMessage() // const message = useMessage() //
const mindmapResult = ref('') // const generatedContent = ref('') //
const leftRef = ref<InstanceType<typeof Left>>() // const leftRef = ref<InstanceType<typeof Left>>() //
const rightRef = ref<InstanceType<typeof Right>>() // const rightRef = ref<InstanceType<typeof Right>>() //
/** 使用已有内容直接生成 **/ /** 使用已有内容直接生成 **/
const directGenerate = (existPrompt: string) => { const directGenerate = (existPrompt: string) => {
isEnd.value = false // falsetruewatch isEnd.value = false // false true watch
mindmapResult.value = existPrompt generatedContent.value = existPrompt
isEnd.value = true isEnd.value = true
} }
@ -58,7 +58,7 @@ const submit = (data: AiMindMapGenerateReqVO) => {
isStart.value = true isStart.value = true
isEnd.value = false isEnd.value = false
ctrl.value = new AbortController() // ctrl.value = new AbortController() //
mindmapResult.value = '' // generatedContent.value = '' //
AiMindMapApi.generateMindMap({ AiMindMapApi.generateMindMap({
data, data,
onMessage: async (res) => { onMessage: async (res) => {
@ -68,13 +68,13 @@ const submit = (data: AiMindMapGenerateReqVO) => {
stopStream() stopStream()
return return
} }
mindmapResult.value = mindmapResult.value + data generatedContent.value = generatedContent.value + data
await nextTick() await nextTick()
rightRef.value?.scrollBottom() rightRef.value?.scrollBottom()
}, },
onClose() { onClose() {
isEnd.value = true isEnd.value = true
leftRef.value?.setGeneratedContent(mindmapResult.value) leftRef.value?.setGeneratedContent(generatedContent.value)
stopStream() stopStream()
}, },
onError(err) { onError(err) {
@ -87,6 +87,6 @@ const submit = (data: AiMindMapGenerateReqVO) => {
/** 初始化 */ /** 初始化 */
onMounted(() => { onMounted(() => {
mindmapResult.value = MindMapContentExample generatedContent.value = MindMapContentExample
}) })
</script> </script>

View File

@ -0,0 +1,189 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form-item label="用户编号" prop="userId">
<el-select
v-model="queryParams.userId"
clearable
placeholder="请输入用户编号"
class="!w-240px"
>
<el-option
v-for="item in userList"
:key="item.id"
:label="item.nickname"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="提示词" prop="prompt">
<el-input
v-model="queryParams.prompt"
placeholder="请输入提示词"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-220px"
/>
</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-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="编号" align="center" prop="id" width="180" fixed="left" />
<el-table-column label="用户" align="center" prop="userId" width="180">
<template #default="scope">
<span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span>
</template>
</el-table-column>
<el-table-column label="提示词" align="center" prop="prompt" width="180" />
<el-table-column label="思维导图" align="center" prop="generatedContent" min-width="300" />
<el-table-column label="模型" align="center" prop="model" width="180" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="错误信息" align="center" prop="errorMessage" />
<el-table-column label="操作" align="center" width="120" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="openPreview(scope.row)"> </el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['ai:mind-map:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 思维导图的预览 -->
<el-drawer v-model="previewVisible" :with-header="false" size="800px">
<Right
v-if="previewVisible2"
:generatedContent="previewContent"
:isEnd="true"
:isGenerating="false"
:isStart="false"
/>
</el-drawer>
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import { AiMindMapApi, MindMapVO } from '@/api/ai/mindmap'
import * as UserApi from '@/api/system/user'
import Right from '@/views/ai/mindmap/index/components/Right.vue'
/** AI 思维导图 列表 */
defineOptions({ name: 'AiMindMapManager' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<MindMapVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
userId: undefined,
prompt: undefined,
createTime: []
})
const queryFormRef = ref() //
const userList = ref<UserApi.UserVO[]>([]) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await AiMindMapApi.getMindMapPage(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 handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await AiMindMapApi.deleteMindMap(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 预览操作按钮 */
const previewVisible = ref(false) // drawer
const previewVisible2 = ref(false) // right
const previewContent = ref('')
const openPreview = async (row: MindMapVO) => {
previewVisible2.value = false
previewVisible.value = true
// drawer right width
await nextTick()
previewVisible2.value = true
previewContent.value = row.generatedContent
}
/** 初始化 **/
onMounted(async () => {
getList()
//
userList.value = await UserApi.getSimpleUserList()
})
</script>

View File

@ -31,7 +31,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -41,7 +41,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -37,7 +37,7 @@
<el-radio <el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
@ -51,7 +51,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
@ -69,7 +69,7 @@ import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict'
import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole' import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole'
import { CommonStatusEnum } from '@/utils/constants' import { CommonStatusEnum } from '@/utils/constants'
import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel' import { ChatModelApi, ChatModelVO } from '@/api/ai/model/chatModel'
import {FormRules} from "element-plus"; import { FormRules } from 'element-plus'
/** AI 聊天角色 表单 */ /** AI 聊天角色 表单 */
defineOptions({ name: 'ChatRoleForm' }) defineOptions({ name: 'ChatRoleForm' })

View File

@ -1,20 +1,14 @@
<template> <template>
<ContentWrap class="w-300px h-full mb-[0!important]"> <ContentWrap class="w-300px h-full mb-[0!important]">
<el-radio-group v-model="generateMode" class="mb-15px"> <el-radio-group v-model="generateMode" class="mb-15px">
<el-radio-button label="desc"> <el-radio-button value="desc"> 描述模式 </el-radio-button>
描述模式 <el-radio-button value="lyric"> 歌词模式 </el-radio-button>
</el-radio-button>
<el-radio-button label="lyric">
歌词模式
</el-radio-button>
</el-radio-group> </el-radio-group>
<!-- 描述模式/歌词模式 切换 --> <!-- 描述模式/歌词模式 切换 -->
<component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef"/> <component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef" />
<el-button type="primary" round class="w-full" @click="generateMusic"> <el-button type="primary" round class="w-full" @click="generateMusic"> </el-button>
创作音乐
</el-button>
</ContentWrap> </ContentWrap>
</template> </template>
@ -34,8 +28,8 @@ const modeRef = ref<Nullable<{ formData: Recordable }>>(null)
*@Description: 根据信息生成音乐 *@Description: 根据信息生成音乐
*@MethodAuthor: xiaohong *@MethodAuthor: xiaohong
*@Date: 2024-06-27 16:40:16 *@Date: 2024-06-27 16:40:16
*/ */
function generateMusic () { function generateMusic() {
emits('generate-music', {formData: unref(modeRef)?.formData}) emits('generate-music', { formData: unref(modeRef)?.formData })
} }
</script> </script>

View File

@ -104,7 +104,7 @@
import { createReusableTemplate } from '@vueuse/core' import { createReusableTemplate } from '@vueuse/core'
import { ref } from 'vue' import { ref } from 'vue'
import Tag from './Tag.vue' import Tag from './Tag.vue'
import { WriteVO } from 'src/api/ai/write' import { WriteVO } from '@/api/ai/write'
import { omit } from 'lodash-es' import { omit } from 'lodash-es'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants' import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants'

View File

@ -18,7 +18,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -22,7 +22,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -28,7 +28,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -62,7 +62,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)" v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -9,7 +9,7 @@
> >
<el-form-item label="减签任务" prop="id"> <el-form-item label="减签任务" prop="id">
<el-radio-group v-model="formData.id"> <el-radio-group v-model="formData.id">
<el-radio-button v-for="item in childrenTaskList" :key="item.id" :label="item.id"> <el-radio-button v-for="item in childrenTaskList" :key="item.id" :value="item.id">
{{ item.name }} {{ item.name }}
({{ item.assigneeUser?.deptName || item.ownerUser?.deptName }} - ({{ item.assigneeUser?.deptName || item.ownerUser?.deptName }} -
{{ item.assigneeUser?.nickname || item.ownerUser?.nickname }}) {{ item.assigneeUser?.nickname || item.ownerUser?.nickname }})

View File

@ -47,10 +47,10 @@
<el-form-item label="抄送人" prop="copyUserIds"> <el-form-item label="抄送人" prop="copyUserIds">
<el-select v-model="auditForms[index].copyUserIds" multiple placeholder="请选择抄送人"> <el-select v-model="auditForms[index].copyUserIds" multiple placeholder="请选择抄送人">
<el-option <el-option
v-for="item in userOptions" v-for="itemx in userOptions"
:key="item.id" :key="itemx.id"
:label="item.nickname" :label="itemx.nickname"
:value="item.id" :value="itemx.id"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -90,7 +90,7 @@
<el-radio <el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -27,8 +27,8 @@
@change="changeNotifyEnable" @change="changeNotifyEnable"
class="ml-4" class="ml-4"
> >
<el-radio :label="false" size="large">不提醒</el-radio> <el-radio :value="false" size="large">不提醒</el-radio>
<el-radio :label="true" size="large">提醒</el-radio> <el-radio :value="true" size="large">提醒</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div v-if="formData.notifyEnabled"> <div v-if="formData.notifyEnabled">

View File

@ -27,8 +27,8 @@
<!-- 表单 --> <!-- 表单 -->
<el-form-item label="客户公海规则设置" prop="enabled"> <el-form-item label="客户公海规则设置" prop="enabled">
<el-radio-group v-model="formData.enabled" @change="changeEnable" class="ml-4"> <el-radio-group v-model="formData.enabled" @change="changeEnable" class="ml-4">
<el-radio :label="false" size="large">不启用</el-radio> <el-radio :value="false" size="large">不启用</el-radio>
<el-radio :label="true" size="large">启用</el-radio> <el-radio :value="true" size="large">启用</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div v-if="formData.enabled"> <div v-if="formData.enabled">
@ -44,8 +44,8 @@
@change="changeNotifyEnable" @change="changeNotifyEnable"
class="ml-4" class="ml-4"
> >
<el-radio :label="false" size="large">不提醒</el-radio> <el-radio :value="false" size="large">不提醒</el-radio>
<el-radio :label="true" size="large">提醒</el-radio> <el-radio :value="true" size="large">提醒</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div v-if="formData.notifyEnabled"> <div v-if="formData.notifyEnabled">

View File

@ -23,7 +23,7 @@
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PERMISSION_LEVEL)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PERMISSION_LEVEL)"
:key="dict.value" :key="dict.value"
> >
<el-radio v-if="dict.value != PermissionLevelEnum.OWNER" :label="dict.value"> <el-radio v-if="dict.value != PermissionLevelEnum.OWNER" :value="dict.value">
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
</template> </template>
@ -34,9 +34,9 @@
label="同时添加至" label="同时添加至"
> >
<el-checkbox-group v-model="formData.toBizTypes"> <el-checkbox-group v-model="formData.toBizTypes">
<el-checkbox :label="BizTypeEnum.CRM_CONTACT">联系人</el-checkbox> <el-checkbox :value="BizTypeEnum.CRM_CONTACT">联系人</el-checkbox>
<el-checkbox :label="BizTypeEnum.CRM_BUSINESS">商机</el-checkbox> <el-checkbox :value="BizTypeEnum.CRM_BUSINESS">商机</el-checkbox>
<el-checkbox :label="BizTypeEnum.CRM_CONTRACT">合同</el-checkbox> <el-checkbox :value="BizTypeEnum.CRM_CONTRACT">合同</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@ -20,8 +20,8 @@
</el-form-item> </el-form-item>
<el-form-item label="老负责人"> <el-form-item label="老负责人">
<el-radio-group v-model="oldOwnerHandler" @change="handleOwnerChange"> <el-radio-group v-model="oldOwnerHandler" @change="handleOwnerChange">
<el-radio :label="false" size="large">移除</el-radio> <el-radio :value="false" size="large">移除</el-radio>
<el-radio :label="true" size="large">加入团队</el-radio> <el-radio :value="true" size="large">加入团队</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item v-if="oldOwnerHandler" label="老负责人权限级别" prop="oldOwnerPermissionLevel"> <el-form-item v-if="oldOwnerHandler" label="老负责人权限级别" prop="oldOwnerPermissionLevel">
@ -30,7 +30,7 @@
v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PERMISSION_LEVEL)" v-for="dict in getIntDictOptions(DICT_TYPE.CRM_PERMISSION_LEVEL)"
:key="dict.value" :key="dict.value"
> >
<el-radio v-if="dict.value != PermissionLevelEnum.OWNER" :label="dict.value"> <el-radio v-if="dict.value != PermissionLevelEnum.OWNER" :value="dict.value">
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>
</template> </template>
@ -38,9 +38,9 @@
</el-form-item> </el-form-item>
<el-form-item v-if="bizType === BizTypeEnum.CRM_CUSTOMER" label="同时转移"> <el-form-item v-if="bizType === BizTypeEnum.CRM_CUSTOMER" label="同时转移">
<el-checkbox-group v-model="formData.toBizTypes"> <el-checkbox-group v-model="formData.toBizTypes">
<el-checkbox :label="BizTypeEnum.CRM_CONTACT">联系人</el-checkbox> <el-checkbox :value="BizTypeEnum.CRM_CONTACT">联系人</el-checkbox>
<el-checkbox :label="BizTypeEnum.CRM_BUSINESS">商机</el-checkbox> <el-checkbox :value="BizTypeEnum.CRM_BUSINESS">商机</el-checkbox>
<el-checkbox :label="BizTypeEnum.CRM_CONTRACT">合同</el-checkbox> <el-checkbox :value="BizTypeEnum.CRM_CONTRACT">合同</el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
</el-form> </el-form>

View File

@ -21,7 +21,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -31,7 +31,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -50,7 +50,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -64,7 +64,7 @@
<el-table-column align="center" width="65"> <el-table-column align="center" width="65">
<template #default="scope"> <template #default="scope">
<el-radio <el-radio
:label="scope.row.id" :value="scope.row.id"
v-model="currentRowValue" v-model="currentRowValue"
@change="handleCurrentChange(scope.row)" @change="handleCurrentChange(scope.row)"
> >

View File

@ -64,7 +64,7 @@
<el-table-column align="center" width="65"> <el-table-column align="center" width="65">
<template #default="scope"> <template #default="scope">
<el-radio <el-radio
:label="scope.row.id" :value="scope.row.id"
v-model="currentRowValue" v-model="currentRowValue"
@change="handleCurrentChange(scope.row)" @change="handleCurrentChange(scope.row)"
> >

View File

@ -44,7 +44,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -44,7 +44,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -64,7 +64,7 @@
<el-table-column align="center" width="65"> <el-table-column align="center" width="65">
<template #default="scope"> <template #default="scope">
<el-radio <el-radio
:label="scope.row.id" :value="scope.row.id"
v-model="currentRowValue" v-model="currentRowValue"
@change="handleCurrentChange(scope.row)" @change="handleCurrentChange(scope.row)"
> >

View File

@ -64,7 +64,7 @@
<el-table-column align="center" width="65"> <el-table-column align="center" width="65">
<template #default="scope"> <template #default="scope">
<el-radio <el-radio
:label="scope.row.id" :value="scope.row.id"
v-model="currentRowValue" v-model="currentRowValue"
@change="handleCurrentChange(scope.row)" @change="handleCurrentChange(scope.row)"
> >

View File

@ -19,7 +19,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -37,26 +37,26 @@
</el-table-column> </el-table-column>
<el-table-column label="插入" min-width="4%"> <el-table-column label="插入" min-width="4%">
<template #default="scope"> <template #default="scope">
<el-checkbox v-model="scope.row.createOperation" false-label="false" true-label="true" /> <el-checkbox v-model="scope.row.createOperation" false-value="false" true-value="true" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="编辑" min-width="4%"> <el-table-column label="编辑" min-width="4%">
<template #default="scope"> <template #default="scope">
<el-checkbox v-model="scope.row.updateOperation" false-label="false" true-label="true" /> <el-checkbox v-model="scope.row.updateOperation" false-value="false" true-value="true" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="列表" min-width="4%"> <el-table-column label="列表" min-width="4%">
<template #default="scope"> <template #default="scope">
<el-checkbox <el-checkbox
v-model="scope.row.listOperationResult" v-model="scope.row.listOperationResult"
false-label="false" false-value="false"
true-label="true" true-value="true"
/> />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="查询" min-width="4%"> <el-table-column label="查询" min-width="4%">
<template #default="scope"> <template #default="scope">
<el-checkbox v-model="scope.row.listOperation" false-label="false" true-label="true" /> <el-checkbox v-model="scope.row.listOperation" false-value="false" true-value="true" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="查询方式" min-width="10%"> <el-table-column label="查询方式" min-width="10%">
@ -75,7 +75,7 @@
</el-table-column> </el-table-column>
<el-table-column label="允许空" min-width="5%"> <el-table-column label="允许空" min-width="5%">
<template #default="scope"> <template #default="scope">
<el-checkbox v-model="scope.row.nullable" false-label="false" true-label="true" /> <el-checkbox v-model="scope.row.nullable" false-value="false" true-value="true" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="显示类型" min-width="12%"> <el-table-column label="显示类型" min-width="12%">

View File

@ -285,8 +285,8 @@
</span> </span>
</template> </template>
<el-radio-group v-model="formData.subJoinMany" placeholder="请选择"> <el-radio-group v-model="formData.subJoinMany" placeholder="请选择">
<el-radio :label="true">一对多</el-radio> <el-radio :value="true">一对多</el-radio>
<el-radio :label="false">一对一</el-radio> <el-radio :value="false">一对一</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-col> </el-col>

View File

@ -24,7 +24,7 @@
<el-radio <el-radio
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
:key="dict.value as string" :key="dict.value as string"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -15,7 +15,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)" v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -66,8 +66,8 @@
</el-form-item> </el-form-item>
<el-form-item v-if="formData.storage === 11" label="连接模式" prop="config.mode"> <el-form-item v-if="formData.storage === 11" label="连接模式" prop="config.mode">
<el-radio-group v-model="formData.config.mode"> <el-radio-group v-model="formData.config.mode">
<el-radio key="Active" label="Active">主动模式</el-radio> <el-radio key="Active" value="Active">主动模式</el-radio>
<el-radio key="Passive" label="Passive">被动模式</el-radio> <el-radio key="Passive" value="Passive">被动模式</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- S3 --> <!-- S3 -->

View File

@ -53,10 +53,18 @@ const data = reactive({
/** 查询订单数据 */ /** 查询订单数据 */
const getOrderData = async () => { const getOrderData = async () => {
const orderCount = await TradeStatisticsApi.getOrderCount() const orderCount = await TradeStatisticsApi.getOrderCount()
data.orderUndelivered.value = orderCount.undelivered if (orderCount.undelivered != null) {
data.orderAfterSaleApply.value = orderCount.afterSaleApply data.orderUndelivered.value = orderCount.undelivered
data.orderWaitePickUp.value = orderCount.pickUp }
data.withdrawAuditing.value = orderCount.auditingWithdraw if (orderCount.afterSaleApply != null) {
data.orderAfterSaleApply.value = orderCount.afterSaleApply
}
if (orderCount.pickUp != null) {
data.orderWaitePickUp.value = orderCount.pickUp
}
if (orderCount.auditingWithdraw != null) {
data.withdrawAuditing.value = orderCount.auditingWithdraw
}
} }
/** 查询商品数据 */ /** 查询商品数据 */
@ -83,6 +91,13 @@ const handleClick = (routerName: string) => {
router.push({ name: routerName }) router.push({ name: routerName })
} }
/** 激活时 */
onActivated(() => {
getOrderData()
getProductData()
getWalletRechargeData()
})
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(() => {
getOrderData() getOrderData()

View File

@ -6,7 +6,7 @@
<!-- 查询条件 --> <!-- 查询条件 -->
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-2">
<el-radio-group v-model="timeRangeType" @change="handleTimeRangeTypeChange"> <el-radio-group v-model="timeRangeType" @change="handleTimeRangeTypeChange">
<el-radio-button v-for="[key, value] in timeRange.entries()" :key="key" :label="key"> <el-radio-button v-for="[key, value] in timeRange.entries()" :key="key" :value="key">
{{ value.name }} {{ value.name }}
</el-radio-button> </el-radio-button>
</el-radio-group> </el-radio-group>

View File

@ -21,7 +21,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -33,7 +33,7 @@
<el-radio <el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-radio> </el-radio>

View File

@ -24,7 +24,7 @@
> >
<template #default="{ row }"> <template #default="{ row }">
<span style="font-weight: bold; color: #40aaff"> <span style="font-weight: bold; color: #40aaff">
{{ row.properties[index]?.valueName }} {{ row.properties?.[index]?.valueName }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
@ -168,7 +168,7 @@
> >
<template #default="{ row }"> <template #default="{ row }">
<span style="font-weight: bold; color: #40aaff"> <span style="font-weight: bold; color: #40aaff">
{{ row.properties[index]?.valueName }} {{ row.properties?.[index]?.valueName }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>
@ -248,7 +248,7 @@
> >
<template #default="{ row }"> <template #default="{ row }">
<span style="font-weight: bold; color: #40aaff"> <span style="font-weight: bold; color: #40aaff">
{{ row.properties[index]?.valueName }} {{ row.properties?.[index]?.valueName }}
</span> </span>
</template> </template>
</el-table-column> </el-table-column>

View File

@ -3,7 +3,7 @@
<el-table v-loading="loading" :data="list" show-overflow-tooltip> <el-table v-loading="loading" :data="list" show-overflow-tooltip>
<el-table-column label="#" width="55"> <el-table-column label="#" width="55">
<template #default="{ row }"> <template #default="{ row }">
<el-radio :label="row.id" v-model="selectedSkuId" @change="handleSelected(row)" <el-radio :value="row.id" v-model="selectedSkuId" @change="handleSelected(row)"
>&nbsp; >&nbsp;
</el-radio> </el-radio>
</template> </template>

View File

@ -70,7 +70,7 @@
<!-- 2. 单选模式 --> <!-- 2. 单选模式 -->
<el-table-column label="#" width="55" v-else> <el-table-column label="#" width="55" v-else>
<template #default="{ row }"> <template #default="{ row }">
<el-radio :label="row.id" v-model="selectedSpuId" @change="handleSingleSelected(row)"> <el-radio :value="row.id" v-model="selectedSpuId" @change="handleSingleSelected(row)">
<!-- 空格不能省略是为了让单选框不显示label如果不指定label不会有选中的效果 --> <!-- 空格不能省略是为了让单选框不显示label如果不指定label不会有选中的效果 -->
&nbsp; &nbsp;
</el-radio> </el-radio>

View File

@ -6,7 +6,7 @@
<el-checkbox <el-checkbox
v-for="dict in getIntDictOptions(DICT_TYPE.TRADE_DELIVERY_TYPE)" v-for="dict in getIntDictOptions(DICT_TYPE.TRADE_DELIVERY_TYPE)"
:key="dict.value" :key="dict.value"
:label="dict.value" :value="dict.value"
> >
{{ dict.label }} {{ dict.label }}
</el-checkbox> </el-checkbox>

View File

@ -18,16 +18,28 @@
> >
{{ value.name }} {{ value.name }}
</el-tag> </el-tag>
<el-input <el-select
v-show="inputVisible(index)"
:id="`input${index}`" :id="`input${index}`"
:ref="setInputRef" :ref="setInputRef"
v-show="inputVisible(index)"
v-model="inputValue" v-model="inputValue"
class="!w-20" filterable
allow-create
default-first-option
:reserve-keyword="false"
size="small" size="small"
class="!w-30"
@blur="handleInputConfirm(index, item.id)" @blur="handleInputConfirm(index, item.id)"
@keyup.enter="handleInputConfirm(index, item.id)" @keyup.enter="handleInputConfirm(index, item.id)"
/> @change="handleInputConfirm(index, item.id)"
>
<el-option
v-for="item2 in attributeOptions"
:key="item2.id"
:label="item2.name"
:value="item2.name"
/>
</el-select>
<el-button <el-button
v-show="!inputVisible(index)" v-show="!inputVisible(index)"
class="button-new-tag ml-1" class="button-new-tag ml-1"
@ -42,7 +54,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ElInput } from 'element-plus'
import * as PropertyApi from '@/api/mall/product/property' import * as PropertyApi from '@/api/mall/product/property'
import { PropertyAndValues } from '@/views/mall/product/spu/components' import { PropertyAndValues } from '@/views/mall/product/spu/components'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
@ -63,11 +74,12 @@ const inputRef = ref<any[]>([]) //标签输入框Ref
const setInputRef = (el: any) => { const setInputRef = (el: any) => {
if (el === null || typeof el === 'undefined') return if (el === null || typeof el === 'undefined') return
// id // id
if (!inputRef.value.some((item) => item.input?.attributes.id === el.input?.attributes.id)) { if (!inputRef.value.some((item) => item.inputRef?.attributes.id === el.inputRef?.attributes.id)) {
inputRef.value.push(el) inputRef.value.push(el)
} }
} }
const attributeList = ref<PropertyAndValues[]>([]) // const attributeList = ref<PropertyAndValues[]>([]) //
const attributeOptions = ref([] as PropertyApi.PropertyValueVO[]) //
const props = defineProps({ const props = defineProps({
propertyList: { propertyList: {
type: Array, type: Array,
@ -100,16 +112,36 @@ const handleCloseProperty = (index: number) => {
} }
/** 显示输入框并获取焦点 */ /** 显示输入框并获取焦点 */
const showInput = async (index) => { const showInput = async (index: number) => {
attributeIndex.value = index attributeIndex.value = index
inputRef.value[index].focus() inputRef.value[index].focus()
//
await getAttributeOptions(attributeList.value[index].id)
} }
/** 输入框失去焦点或点击回车时触发 */ /** 输入框失去焦点或点击回车时触发 */
const emit = defineEmits(['success']) // success const emit = defineEmits(['success']) // success
const handleInputConfirm = async (index: number, propertyId: number) => { const handleInputConfirm = async (index: number, propertyId: number) => {
if (inputValue.value) { if (inputValue.value) {
// // 1.
if (attributeList.value[index].values.find((item) => item.name === inputValue.value)) {
message.warning('已存在相同属性值,请重试')
attributeIndex.value = null
inputValue.value = ''
return
}
// 2.1 使
const existValue = attributeOptions.value.find((item) => item.name === inputValue.value)
if (existValue) {
attributeIndex.value = null
inputValue.value = ''
attributeList.value[index].values.push({ id: existValue.id, name: existValue.name })
emit('success', attributeList.value)
return
}
// 2.2
try { try {
const id = await PropertyApi.createPropertyValue({ propertyId, name: inputValue.value }) const id = await PropertyApi.createPropertyValue({ propertyId, name: inputValue.value })
attributeList.value[index].values.push({ id, name: inputValue.value }) attributeList.value[index].values.push({ id, name: inputValue.value })
@ -122,4 +154,9 @@ const handleInputConfirm = async (index: number, propertyId: number) => {
attributeIndex.value = null attributeIndex.value = null
inputValue.value = '' inputValue.value = ''
} }
/** 获取商品属性下拉选项 */
const getAttributeOptions = async (propertyId: number) => {
attributeOptions.value = await PropertyApi.getPropertyValueSimpleList(propertyId)
}
</script> </script>

View File

@ -10,7 +10,22 @@
@keydown.enter.prevent="submitForm" @keydown.enter.prevent="submitForm"
> >
<el-form-item label="属性名称" prop="name"> <el-form-item label="属性名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入名称" /> <el-select
v-model="formData.name"
filterable
allow-create
default-first-option
:reserve-keyword="false"
placeholder="请选择属性名称。如果不存在,可手动输入选择"
class="!w-360px"
>
<el-option
v-for="item in attributeOptions"
:key="item.id"
:label="item.name"
:value="item.name"
/>
</el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -37,6 +52,7 @@ const formRules = reactive({
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
const attributeList = ref([]) // const attributeList = ref([]) //
const attributeOptions = ref([] as PropertyApi.PropertyVO[]) //
const props = defineProps({ const props = defineProps({
propertyList: { propertyList: {
type: Array, type: Array,
@ -60,15 +76,39 @@ watch(
const open = async () => { const open = async () => {
dialogVisible.value = true dialogVisible.value = true
resetForm() resetForm()
//
await getAttributeOptions()
} }
defineExpose({ open }) // open defineExpose({ open }) // open
/** 提交表单 */ /** 提交表单 */
const submitForm = async () => { const submitForm = async () => {
// // 1.1
for (const attrItem of attributeList.value) {
if (attrItem.name === formData.value.name) {
return message.error('该属性已存在,请勿重复添加')
}
}
// 1.2
if (!formRef) return if (!formRef) return
const valid = await formRef.value.validate() const valid = await formRef.value.validate()
if (!valid) return if (!valid) return
// 2.1 使
const existProperty = attributeOptions.value.find((item) => item.name === formData.value.name)
if (existProperty) {
//
attributeList.value.push({
id: existProperty.id,
...formData.value,
values: []
})
//
dialogVisible.value = false
return
}
// 2.2
// //
formLoading.value = true formLoading.value = true
try { try {
@ -80,6 +120,7 @@ const submitForm = async () => {
...formData.value, ...formData.value,
values: [] values: []
}) })
//
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
dialogVisible.value = false dialogVisible.value = false
} finally { } finally {
@ -94,4 +135,14 @@ const resetForm = () => {
} }
formRef.value?.resetFields() formRef.value?.resetFields()
} }
/** 获取商品属性下拉选项 */
const getAttributeOptions = async () => {
formLoading.value = true
try {
attributeOptions.value = await PropertyApi.getPropertySimpleList()
} finally {
formLoading.value = false
}
}
</script> </script>

Some files were not shown because too many files have changed in this diff Show More