mirror of
https://gitee.com/hhyykk/ipms-sjy-ui.git
synced 2025-06-19 06:42:00 +08:00
【代码优化】分支合并
This commit is contained in:
commit
ffdd3325dd
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -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"
|
||||||
|
@ -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",
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
// 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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 } })
|
||||||
|
}
|
||||||
|
@ -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 // 成本价
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import { defineComponent, PropType, ref } from 'vue'
|
import { computed, defineComponent, PropType } 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, isBoolean, isNumber, isString } 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>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
<el-scrollbar ref="containerRef" class="z-1 min-h-30px" wrap-class="w-full">
|
||||||
<!-- 商品网格 -->
|
<!-- 商品网格 -->
|
||||||
<div
|
<div
|
||||||
class="grid overflow-x-auto"
|
|
||||||
:style="{
|
:style="{
|
||||||
gridGap: `${property.space}px`,
|
gridGap: `${property.space}px`,
|
||||||
gridTemplateColumns,
|
gridTemplateColumns,
|
||||||
width: scrollbarWidth
|
width: scrollbarWidth
|
||||||
}"
|
}"
|
||||||
|
class="grid overflow-x-auto"
|
||||||
>
|
>
|
||||||
<!-- 商品 -->
|
<!-- 商品 -->
|
||||||
<div
|
<div
|
||||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
v-for="(spu, index) in spuList"
|
||||||
|
:key="index"
|
||||||
:style="{
|
:style="{
|
||||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||||
}"
|
}"
|
||||||
v-for="(spu, index) in spuList"
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
:key="index"
|
|
||||||
>
|
>
|
||||||
<!-- 角标 -->
|
<!-- 角标 -->
|
||||||
<div
|
<div
|
||||||
v-if="property.badge.show"
|
v-if="property.badge.show"
|
||||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||||
>
|
>
|
||||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
<el-image :src="property.badge.imgUrl" class="h-26px w-38px" fit="cover" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 商品封面图 -->
|
<!-- 商品封面图 -->
|
||||||
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
<el-image :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" fit="cover" />
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'flex flex-col gap-8px p-8px box-border',
|
'flex flex-col gap-8px p-8px box-border',
|
||||||
@ -42,8 +42,8 @@
|
|||||||
<!-- 商品名称 -->
|
<!-- 商品名称 -->
|
||||||
<div
|
<div
|
||||||
v-if="property.fields.name.show"
|
v-if="property.fields.name.show"
|
||||||
class="truncate text-12px"
|
|
||||||
:style="{ color: property.fields.name.color }"
|
:style="{ color: property.fields.name.color }"
|
||||||
|
class="truncate text-12px"
|
||||||
>
|
>
|
||||||
{{ spu.name }}
|
{{ spu.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -51,10 +51,10 @@
|
|||||||
<!-- 商品价格 -->
|
<!-- 商品价格 -->
|
||||||
<span
|
<span
|
||||||
v-if="property.fields.price.show"
|
v-if="property.fields.price.show"
|
||||||
class="text-12px"
|
|
||||||
:style="{ color: property.fields.price.color }"
|
:style="{ color: property.fields.price.color }"
|
||||||
|
class="text-12px"
|
||||||
>
|
>
|
||||||
¥{{ spu.price }}
|
¥{{ fenToYuan(spu.combinationPrice || spu.price || 0) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,10 +62,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
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 { Spu } from '@/api/mall/product/spu'
|
||||||
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
|
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,
|
||||||
@ -122,4 +132,4 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
<el-scrollbar ref="containerRef" class="z-1 min-h-30px" wrap-class="w-full">
|
||||||
<!-- 商品网格 -->
|
<!-- 商品网格 -->
|
||||||
<div
|
<div
|
||||||
class="grid overflow-x-auto"
|
|
||||||
:style="{
|
:style="{
|
||||||
gridGap: `${property.space}px`,
|
gridGap: `${property.space}px`,
|
||||||
gridTemplateColumns,
|
gridTemplateColumns,
|
||||||
width: scrollbarWidth
|
width: scrollbarWidth
|
||||||
}"
|
}"
|
||||||
|
class="grid overflow-x-auto"
|
||||||
>
|
>
|
||||||
<!-- 商品 -->
|
<!-- 商品 -->
|
||||||
<div
|
<div
|
||||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
v-for="(spu, index) in spuList"
|
||||||
|
:key="index"
|
||||||
:style="{
|
:style="{
|
||||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||||
}"
|
}"
|
||||||
v-for="(spu, index) in spuList"
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
:key="index"
|
|
||||||
>
|
>
|
||||||
<!-- 角标 -->
|
<!-- 角标 -->
|
||||||
<div
|
<div
|
||||||
v-if="property.badge.show"
|
v-if="property.badge.show"
|
||||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
class="absolute left-0 top-0 z-1 items-center justify-center"
|
||||||
>
|
>
|
||||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
<el-image :src="property.badge.imgUrl" class="h-26px w-38px" fit="cover" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 商品封面图 -->
|
<!-- 商品封面图 -->
|
||||||
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
<el-image :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" fit="cover" />
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
'flex flex-col gap-8px p-8px box-border',
|
'flex flex-col gap-8px p-8px box-border',
|
||||||
@ -42,8 +42,8 @@
|
|||||||
<!-- 商品名称 -->
|
<!-- 商品名称 -->
|
||||||
<div
|
<div
|
||||||
v-if="property.fields.name.show"
|
v-if="property.fields.name.show"
|
||||||
class="truncate text-12px"
|
|
||||||
:style="{ color: property.fields.name.color }"
|
:style="{ color: property.fields.name.color }"
|
||||||
|
class="truncate text-12px"
|
||||||
>
|
>
|
||||||
{{ spu.name }}
|
{{ spu.name }}
|
||||||
</div>
|
</div>
|
||||||
@ -51,10 +51,10 @@
|
|||||||
<!-- 商品价格 -->
|
<!-- 商品价格 -->
|
||||||
<span
|
<span
|
||||||
v-if="property.fields.price.show"
|
v-if="property.fields.price.show"
|
||||||
class="text-12px"
|
|
||||||
:style="{ color: property.fields.price.color }"
|
:style="{ color: property.fields.price.color }"
|
||||||
|
class="text-12px"
|
||||||
>
|
>
|
||||||
¥{{ spu.price }}
|
¥{{ fenToYuan(spu.seckillPrice || spu.price || 0) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -62,10 +62,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
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 { Spu } from '@/api/mall/product/spu'
|
||||||
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||||
|
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,
|
||||||
@ -122,4 +132,4 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style lang="scss" scoped></style>
|
||||||
|
@ -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
|
||||||
|
@ -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" />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, watch, computed, unref, ref, nextTick } from 'vue'
|
import { computed, nextTick, onMounted, ref, unref, watch } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
|
import type { RouteLocationNormalizedLoaded, RouterLinkProps } from 'vue-router'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { usePermissionStore } from '@/store/modules/permission'
|
import { usePermissionStore } from '@/store/modules/permission'
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||||
import { useAppStore } from '@/store/modules/app'
|
import { useAppStore } from '@/store/modules/app'
|
||||||
@ -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,21 +268,33 @@ 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)"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ep:d-arrow-left"
|
|
||||||
color="var(--el-text-color-placeholder)"
|
|
||||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
|
color="var(--el-text-color-placeholder)"
|
||||||
|
icon="ep:d-arrow-left"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<div class="flex-1 overflow-hidden">
|
<div class="flex-1 overflow-hidden">
|
||||||
<ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
|
<ElScrollbar ref="scrollbarRef" class="h-full" @scroll="scroll">
|
||||||
<div class="h-full flex">
|
<div class="h-full flex">
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
|
v-for="item in visitedViews"
|
||||||
|
:key="item.fullPath"
|
||||||
:ref="itemRefs.set"
|
:ref="itemRefs.set"
|
||||||
|
:class="[
|
||||||
|
`${prefixCls}__item`,
|
||||||
|
tagsViewImmerse ? `${prefixCls}__item--immerse` : '',
|
||||||
|
tagsViewIcon ? `${prefixCls}__item--icon` : '',
|
||||||
|
tagsViewImmerse && tagsViewIcon ? `${prefixCls}__item--immerse--icon` : '',
|
||||||
|
item?.meta?.affix ? `${prefixCls}__item--affix` : '',
|
||||||
|
{
|
||||||
|
'is-active': isActive(item)
|
||||||
|
}
|
||||||
|
]"
|
||||||
:schema="[
|
:schema="[
|
||||||
{
|
{
|
||||||
icon: 'ep:refresh',
|
icon: 'ep:refresh',
|
||||||
@ -338,41 +352,33 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
v-for="item in visitedViews"
|
|
||||||
:key="item.fullPath"
|
|
||||||
:tag-item="item"
|
:tag-item="item"
|
||||||
:class="[
|
|
||||||
`${prefixCls}__item`,
|
|
||||||
item?.meta?.affix ? `${prefixCls}__item--affix` : '',
|
|
||||||
{
|
|
||||||
'is-active': isActive(item)
|
|
||||||
}
|
|
||||||
]"
|
|
||||||
@visible-change="visibleChange"
|
@visible-change="visibleChange"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<router-link :ref="tagLinksRefs.set" :to="{ ...item }" custom v-slot="{ navigate }">
|
<router-link :ref="tagLinksRefs.set" v-slot="{ navigate }" :to="{ ...item }" custom>
|
||||||
<div
|
<div
|
||||||
|
:class="`h-full flex items-center justify-center whitespace-nowrap pl-15px ${prefixCls}__item--label`"
|
||||||
@click="navigate"
|
@click="navigate"
|
||||||
class="h-full flex items-center justify-center whitespace-nowrap pl-15px"
|
|
||||||
>
|
>
|
||||||
<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"
|
||||||
/>
|
/>
|
||||||
{{ t(item?.meta?.title as string) }}
|
{{ t(item?.meta?.title as string) }}
|
||||||
<Icon
|
<Icon
|
||||||
:class="`${prefixCls}__item--close`"
|
:class="`${prefixCls}__item--close`"
|
||||||
|
:size="12"
|
||||||
color="#333"
|
color="#333"
|
||||||
icon="ep:close"
|
icon="ep:close"
|
||||||
:size="12"
|
|
||||||
@click.prevent.stop="closeSelectedTag(item)"
|
@click.prevent.stop="closeSelectedTag(item)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -383,29 +389,28 @@ 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)"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ep:d-arrow-right"
|
|
||||||
color="var(--el-text-color-placeholder)"
|
|
||||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
|
color="var(--el-text-color-placeholder)"
|
||||||
|
icon="ep:d-arrow-right"
|
||||||
/>
|
/>
|
||||||
</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)"
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
icon="ep:refresh-right"
|
|
||||||
color="var(--el-text-color-placeholder)"
|
|
||||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
|
color="var(--el-text-color-placeholder)"
|
||||||
|
icon="ep:refresh-right"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<ContextMenu
|
<ContextMenu
|
||||||
trigger="click"
|
|
||||||
:schema="[
|
:schema="[
|
||||||
{
|
{
|
||||||
icon: 'ep:refresh',
|
icon: 'ep:refresh',
|
||||||
@ -457,15 +462,16 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
|
trigger="click"
|
||||||
>
|
>
|
||||||
<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
|
||||||
icon="ep:menu"
|
|
||||||
color="var(--el-text-color-placeholder)"
|
|
||||||
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
:hover-color="isDark ? '#fff' : 'var(--el-color-black)'"
|
||||||
|
color="var(--el-text-color-placeholder)"
|
||||||
|
icon="ep:menu"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
@ -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;
|
||||||
@ -525,6 +531,7 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||||||
display: none;
|
display: none;
|
||||||
transform: translate(0, -50%);
|
transform: translate(0, -50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:not(.#{$prefix-cls}__item--affix):hover {
|
&:not(.#{$prefix-cls}__item--affix):hover {
|
||||||
.#{$prefix-cls}__item--close {
|
.#{$prefix-cls}__item--close {
|
||||||
display: block;
|
display: block;
|
||||||
@ -532,6 +539,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);
|
||||||
@ -542,12 +553,47 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||||||
color: var(--el-color-white);
|
color: var(--el-color-white);
|
||||||
background-color: var(--el-color-primary);
|
background-color: var(--el-color-primary);
|
||||||
border: 1px solid var(--el-color-primary);
|
border: 1px solid var(--el-color-primary);
|
||||||
|
|
||||||
.#{$prefix-cls}__item--close {
|
.#{$prefix-cls}__item--close {
|
||||||
:deep(span) {
|
:deep(span) {
|
||||||
color: var(--el-color-white) !important;
|
color: var(--el-color-white) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__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 {
|
||||||
@ -574,6 +620,7 @@ $prefix-cls: #{$namespace}-tags-view;
|
|||||||
color: var(--el-color-white);
|
color: var(--el-color-white);
|
||||||
background-color: var(--el-color-primary);
|
background-color: var(--el-color-primary);
|
||||||
border: 1px solid var(--el-color-primary);
|
border: 1px solid var(--el-color-primary);
|
||||||
|
|
||||||
.#{$prefix-cls}__item--close {
|
.#{$prefix-cls}__item--close {
|
||||||
:deep(span) {
|
:deep(span) {
|
||||||
color: var(--el-color-white) !important;
|
color: var(--el-color-white) !important;
|
||||||
|
@ -92,6 +92,7 @@ export default {
|
|||||||
localeIcon: '多语言图标',
|
localeIcon: '多语言图标',
|
||||||
messageIcon: '消息图标',
|
messageIcon: '消息图标',
|
||||||
tagsView: '标签页',
|
tagsView: '标签页',
|
||||||
|
tagsViewImmerse: '标签页沉浸',
|
||||||
logo: '标志',
|
logo: '标志',
|
||||||
greyMode: '灰色模式',
|
greyMode: '灰色模式',
|
||||||
fixedHeader: '固定头部',
|
fixedHeader: '固定头部',
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { store } from '../index'
|
import { store } from '../index'
|
||||||
import { setCssVar, humpToUnderline } from '@/utils'
|
import { humpToUnderline, setCssVar } from '@/utils'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||||
import { ElementPlusSize } from '@/types/elementPlus'
|
import { ElementPlusSize } from '@/types/elementPlus'
|
||||||
@ -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
|
||||||
},
|
},
|
||||||
|
@ -40,10 +40,12 @@ export const usePermissionStore = defineStore('permission', {
|
|||||||
}
|
}
|
||||||
const routerMap: AppRouteRecordRaw[] = generateRoute(res)
|
const routerMap: AppRouteRecordRaw[] = generateRoute(res)
|
||||||
// 动态路由,404一定要放到最后面
|
// 动态路由,404一定要放到最后面
|
||||||
|
// preschooler:vue-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,
|
||||||
|
@ -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 {
|
||||||
|
@ -4,3 +4,14 @@
|
|||||||
// .dark .dark\:text-color {
|
// .dark .dark\:text-color {
|
||||||
// color: rgba(255, 255, 255, var(--dark-text-color));
|
// color: rgba(255, 255, 255, var(--dark-text-color));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
// 登录页
|
||||||
|
.dark .login-form {
|
||||||
|
.el-divider__text {
|
||||||
|
background-color: var(--login-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-card {
|
||||||
|
background-color: var(--login-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)" />
|
||||||
|
@ -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
|
||||||
@ -112,9 +114,9 @@
|
|||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :offset="6" :span="12">
|
<el-col :offset="6" :span="12">
|
||||||
<el-link style="float: right" type="primary">{{
|
<el-link style="float: right" type="primary"
|
||||||
t('login.forgetPassword')
|
>{{ t('login.forgetPassword') }}
|
||||||
}}</el-link>
|
</el-link>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-row v-show="getShow" style="margin-right: -10px; margin-left: -10px">
|
<el-row v-show="getShow" class="login-form" 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>
|
||||||
|
@ -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"
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
|
<h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
|
||||||
<span>思维导图预览</span>
|
<span>思维导图预览</span>
|
||||||
<!-- 展示在右上角 -->
|
<!-- 展示在右上角 -->
|
||||||
<el-button type="primary" v-show="isEnd" @click="downloadImage" size="small">
|
<el-button v-show="isEnd" size="small" type="primary" @click="downloadImage">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Icon icon="ph:copy-bold" />
|
<Icon icon="ph:copy-bold" />
|
||||||
</template>
|
</template>
|
||||||
@ -20,14 +20,14 @@
|
|||||||
</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" :style="{ height: `${contentAreaHeight}px` }" class="w-full" />
|
||||||
<div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
|
<div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { Markmap } from 'markmap-view'
|
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'
|
||||||
@ -43,7 +43,7 @@ const props = defineProps<{
|
|||||||
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
|
||||||
@ -106,8 +106,7 @@ const processContent = (text: string) => {
|
|||||||
return arr.join('\n')
|
return arr.join('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 下载图片 */
|
/** 下载图片:download SVG to png file */
|
||||||
// download SVG to png file
|
|
||||||
const downloadImage = () => {
|
const downloadImage = () => {
|
||||||
const svgElement = mindMapRef.value
|
const svgElement = mindMapRef.value
|
||||||
// 将 SVG 渲染到图片对象
|
// 将 SVG 渲染到图片对象
|
||||||
@ -138,6 +137,7 @@ defineExpose({
|
|||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.my-card {
|
.my-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -150,13 +150,16 @@ defineExpose({
|
|||||||
@extend .hide-scroll-bar;
|
@extend .hide-scroll-bar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// markmap的tool样式覆盖
|
// markmap的tool样式覆盖
|
||||||
:deep(.markmap) {
|
:deep(.markmap) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.mm-toolbar-brand) {
|
:deep(.mm-toolbar-brand) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.mm-toolbar) {
|
:deep(.mm-toolbar) {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
<!--表单区域-->
|
<!--表单区域-->
|
||||||
<Left
|
<Left
|
||||||
ref="leftRef"
|
ref="leftRef"
|
||||||
|
:is-generating="isGenerating"
|
||||||
@submit="submit"
|
@submit="submit"
|
||||||
@direct-generate="directGenerate"
|
@direct-generate="directGenerate"
|
||||||
:is-generating="isGenerating"
|
|
||||||
/>
|
/>
|
||||||
<!--右边生成思维导图区域-->
|
<!--右边生成思维导图区域-->
|
||||||
<Right
|
<Right
|
||||||
@ -18,7 +18,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import Left from './components/Left.vue'
|
import Left from './components/Left.vue'
|
||||||
import Right from './components/Right.vue'
|
import Right from './components/Right.vue'
|
||||||
import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap'
|
import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap'
|
||||||
@ -40,7 +40,7 @@ const rightRef = ref<InstanceType<typeof Right>>() // 右边组件
|
|||||||
|
|
||||||
/** 使用已有内容直接生成 **/
|
/** 使用已有内容直接生成 **/
|
||||||
const directGenerate = (existPrompt: string) => {
|
const directGenerate = (existPrompt: string) => {
|
||||||
isEnd.value = false // 先设置为false再设置为true,让子组建的watch能够监听到
|
isEnd.value = false // 先设置为 false 再设置为 true,让子组建的 watch 能够监听到
|
||||||
generatedContent.value = existPrompt
|
generatedContent.value = existPrompt
|
||||||
isEnd.value = true
|
isEnd.value = true
|
||||||
}
|
}
|
||||||
|
195
src/views/ai/mindmap/manager/index.vue
Normal file
195
src/views/ai/mindmap/manager/index.vue
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<!-- 搜索工作栏 -->
|
||||||
|
<el-form
|
||||||
|
ref="queryFormRef"
|
||||||
|
:inline="true"
|
||||||
|
:model="queryParams"
|
||||||
|
class="-mb-15px"
|
||||||
|
label-width="68px"
|
||||||
|
>
|
||||||
|
<el-form-item label="用户编号" prop="userId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.userId"
|
||||||
|
class="!w-240px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入用户编号"
|
||||||
|
>
|
||||||
|
<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"
|
||||||
|
class="!w-240px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入提示词"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="创建时间" prop="createTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.createTime"
|
||||||
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
|
class="!w-220px"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="handleQuery">
|
||||||
|
<Icon class="mr-5px" icon="ep:search" />
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="resetQuery">
|
||||||
|
<Icon class="mr-5px" icon="ep:refresh" />
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||||
|
<el-table-column align="center" fixed="left" label="编号" prop="id" width="180" />
|
||||||
|
<el-table-column align="center" label="用户" 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 align="center" label="提示词" prop="prompt" width="180" />
|
||||||
|
<el-table-column align="center" label="思维导图" min-width="300" prop="generatedContent" />
|
||||||
|
<el-table-column align="center" label="模型" prop="model" width="180" />
|
||||||
|
<el-table-column
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
align="center"
|
||||||
|
label="创建时间"
|
||||||
|
prop="createTime"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
<el-table-column align="center" label="错误信息" prop="errorMessage" />
|
||||||
|
<el-table-column align="center" fixed="right" label="操作" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="openPreview(scope.row)"> 预览</el-button>
|
||||||
|
<el-button
|
||||||
|
v-hasPermi="['ai:mind-map:delete']"
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(scope.row.id)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
:total="total"
|
||||||
|
@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 lang="ts" setup>
|
||||||
|
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>
|
@ -2,8 +2,8 @@
|
|||||||
<!-- 定义 tab 组件:撰写/回复等 -->
|
<!-- 定义 tab 组件:撰写/回复等 -->
|
||||||
<DefineTab v-slot="{ active, text, itemClick }">
|
<DefineTab v-slot="{ active, text, itemClick }">
|
||||||
<span
|
<span
|
||||||
class="inline-block w-1/2 rounded-full cursor-pointer text-center leading-[30px] relative z-1 text-[5C6370] hover:text-black"
|
|
||||||
:class="active ? 'text-black shadow-md' : 'hover:bg-[#DDDFE3]'"
|
:class="active ? 'text-black shadow-md' : 'hover:bg-[#DDDFE3]'"
|
||||||
|
class="inline-block w-1/2 rounded-full cursor-pointer text-center leading-[30px] relative z-1 text-[5C6370] hover:text-black"
|
||||||
@click="itemClick"
|
@click="itemClick"
|
||||||
>
|
>
|
||||||
{{ text }}
|
{{ text }}
|
||||||
@ -14,9 +14,9 @@
|
|||||||
<h3 class="mt-5 mb-3 flex items-center justify-between text-[14px]">
|
<h3 class="mt-5 mb-3 flex items-center justify-between text-[14px]">
|
||||||
<span>{{ label }}</span>
|
<span>{{ label }}</span>
|
||||||
<span
|
<span
|
||||||
@click="hintClick"
|
|
||||||
v-if="hint"
|
v-if="hint"
|
||||||
class="flex items-center text-[12px] text-[#846af7] cursor-pointer select-none"
|
class="flex items-center text-[12px] text-[#846af7] cursor-pointer select-none"
|
||||||
|
@click="hintClick"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:question-filled" />
|
<Icon icon="ep:question-filled" />
|
||||||
{{ hint }}
|
{{ hint }}
|
||||||
@ -29,17 +29,17 @@
|
|||||||
<div class="w-full pt-2 bg-[#f5f7f9] flex justify-center">
|
<div class="w-full pt-2 bg-[#f5f7f9] flex justify-center">
|
||||||
<div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10">
|
<div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10">
|
||||||
<div
|
<div
|
||||||
class="flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
|
|
||||||
:class="
|
:class="
|
||||||
selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]'
|
selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]'
|
||||||
"
|
"
|
||||||
|
class="flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full"
|
||||||
>
|
>
|
||||||
<ReuseTab
|
<ReuseTab
|
||||||
v-for="tab in tabs"
|
v-for="tab in tabs"
|
||||||
:key="tab.value"
|
:key="tab.value"
|
||||||
:text="tab.text"
|
|
||||||
:active="tab.value === selectedTab"
|
:active="tab.value === selectedTab"
|
||||||
:itemClick="() => switchTab(tab.value)"
|
:itemClick="() => switchTab(tab.value)"
|
||||||
|
:text="tab.text"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -49,36 +49,36 @@
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<template v-if="selectedTab === 1">
|
<template v-if="selectedTab === 1">
|
||||||
<ReuseLabel label="写作内容" hint="示例" :hint-click="() => example('write')" />
|
<ReuseLabel :hint-click="() => example('write')" hint="示例" label="写作内容" />
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
|
||||||
:rows="5"
|
|
||||||
:maxlength="500"
|
|
||||||
v-model="formData.prompt"
|
v-model="formData.prompt"
|
||||||
|
:maxlength="500"
|
||||||
|
:rows="5"
|
||||||
placeholder="请输入写作内容"
|
placeholder="请输入写作内容"
|
||||||
showWordLimit
|
showWordLimit
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<ReuseLabel label="原文" hint="示例" :hint-click="() => example('reply')" />
|
<ReuseLabel :hint-click="() => example('reply')" hint="示例" label="原文" />
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
|
||||||
:rows="5"
|
|
||||||
:maxlength="500"
|
|
||||||
v-model="formData.originalContent"
|
v-model="formData.originalContent"
|
||||||
|
:maxlength="500"
|
||||||
|
:rows="5"
|
||||||
placeholder="请输入原文"
|
placeholder="请输入原文"
|
||||||
showWordLimit
|
showWordLimit
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ReuseLabel label="回复内容" />
|
<ReuseLabel label="回复内容" />
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
|
||||||
:rows="5"
|
|
||||||
:maxlength="500"
|
|
||||||
v-model="formData.prompt"
|
v-model="formData.prompt"
|
||||||
|
:maxlength="500"
|
||||||
|
:rows="5"
|
||||||
placeholder="请输入回复内容"
|
placeholder="请输入回复内容"
|
||||||
showWordLimit
|
showWordLimit
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -93,18 +93,18 @@
|
|||||||
|
|
||||||
<div class="flex items-center justify-center mt-3">
|
<div class="flex items-center justify-center mt-3">
|
||||||
<el-button :disabled="isWriting" @click="reset">重置</el-button>
|
<el-button :disabled="isWriting" @click="reset">重置</el-button>
|
||||||
<el-button :loading="isWriting" @click="submit" color="#846af7">生成</el-button>
|
<el-button :loading="isWriting" color="#846af7" @click="submit">生成</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
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'
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
@click="handleClick(item.routerName)"
|
@click="handleClick(item.routerName)"
|
||||||
>
|
>
|
||||||
<CountTo
|
<CountTo
|
||||||
:prefix="item.prefix"
|
|
||||||
:end-val="item.value"
|
|
||||||
:decimals="item.decimals"
|
:decimals="item.decimals"
|
||||||
|
:end-val="item.value"
|
||||||
|
:prefix="item.prefix"
|
||||||
class="text-3xl"
|
class="text-3xl"
|
||||||
/>
|
/>
|
||||||
<span class="text-center">{{ item.name }}</span>
|
<span class="text-center">{{ item.name }}</span>
|
||||||
@ -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()
|
||||||
|
@ -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>
|
||||||
|
@ -18,16 +18,28 @@
|
|||||||
>
|
>
|
||||||
{{ value.name }}
|
{{ value.name }}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
<el-input
|
<el-select
|
||||||
v-show="inputVisible(index)"
|
v-show="inputVisible(index)"
|
||||||
:id="`input${index}`"
|
:id="`input${index}`"
|
||||||
:ref="setInputRef"
|
:ref="setInputRef"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
class="!w-20"
|
:reserve-keyword="false"
|
||||||
|
allow-create
|
||||||
|
class="!w-30"
|
||||||
|
default-first-option
|
||||||
|
filterable
|
||||||
size="small"
|
size="small"
|
||||||
@blur="handleInputConfirm(index, item.id)"
|
@blur="handleInputConfirm(index, item.id)"
|
||||||
|
@change="handleInputConfirm(index, item.id)"
|
||||||
@keyup.enter="handleInputConfirm(index, item.id)"
|
@keyup.enter="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>
|
||||||
|
@ -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"
|
||||||
|
:reserve-keyword="false"
|
||||||
|
allow-create
|
||||||
|
class="!w-360px"
|
||||||
|
default-first-option
|
||||||
|
filterable
|
||||||
|
placeholder="请选择属性名称。如果不存在,可手动输入选择"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
<!-- 商品发布 - 库存价格 -->
|
<!-- 商品发布 - 库存价格 -->
|
||||||
<template>
|
<template>
|
||||||
<el-form ref="formRef" :disabled="isDetail" :model="formData" :rules="rules" label-width="120px">
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
v-loading="formLoading"
|
||||||
|
:disabled="isDetail"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
label-width="120px"
|
||||||
|
>
|
||||||
<el-form-item label="分销类型" props="subCommissionType">
|
<el-form-item label="分销类型" props="subCommissionType">
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="formData.subCommissionType"
|
v-model="formData.subCommissionType"
|
||||||
@ -94,7 +101,7 @@ const ruleConfig: RuleConfig[] = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
const formLoading = ref(false)
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
propFormData: {
|
propFormData: {
|
||||||
type: Object as PropType<Spu>,
|
type: Object as PropType<Spu>,
|
||||||
|
@ -25,7 +25,7 @@ import OrderBrowsingHistory from './OrderBrowsingHistory.vue'
|
|||||||
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
import { KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
|
||||||
import { isEmpty } from '@/utils/is'
|
import { isEmpty } from '@/utils/is'
|
||||||
import { debounce } from 'lodash-es'
|
import { debounce } from 'lodash-es'
|
||||||
import { ElScrollbar as ElScrollbarType } from 'element-plus/es/components/scrollbar'
|
import { ElScrollbar as ElScrollbarType } from 'element-plus/es/components/scrollbar/index'
|
||||||
|
|
||||||
defineOptions({ name: 'MemberBrowsingHistory' })
|
defineOptions({ name: 'MemberBrowsingHistory' })
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" label="订单金额" prop="refundPrice">
|
<el-table-column align="center" label="订单金额" min-width="120" prop="refundPrice">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<span>{{ fenToYuan(scope.row.refundPrice) }} 元</span>
|
<span>{{ fenToYuan(scope.row.refundPrice) }} 元</span>
|
||||||
</template>
|
</template>
|
||||||
@ -156,7 +156,7 @@
|
|||||||
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
|
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column align="center" fixed="right" label="操作" width="120">
|
<el-table-column align="center" fixed="right" label="操作" width="160">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button link type="primary" @click="openAfterSaleDetail(row.id)">处理退款</el-button>
|
<el-button link type="primary" @click="openAfterSaleDetail(row.id)">处理退款</el-button>
|
||||||
</template>
|
</template>
|
||||||
@ -181,9 +181,6 @@ import { cloneDeep } from 'lodash-es'
|
|||||||
import { fenToYuan } from '@/utils'
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
defineOptions({ name: 'TradeAfterSale' })
|
defineOptions({ name: 'TradeAfterSale' })
|
||||||
const props = defineProps<{
|
|
||||||
userId?: number
|
|
||||||
}>()
|
|
||||||
|
|
||||||
const { push } = useRouter() // 路由跳转
|
const { push } = useRouter() // 路由跳转
|
||||||
|
|
||||||
@ -207,9 +204,9 @@ const queryParams = reactive({
|
|||||||
spuName: null,
|
spuName: null,
|
||||||
createTime: [],
|
createTime: [],
|
||||||
way: null,
|
way: null,
|
||||||
type: null,
|
type: null
|
||||||
userId: null
|
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -219,27 +216,27 @@ const getList = async () => {
|
|||||||
if (data.status === '0') {
|
if (data.status === '0') {
|
||||||
delete data.status
|
delete data.status
|
||||||
}
|
}
|
||||||
if (props.userId) {
|
|
||||||
data.userId = props.userId
|
|
||||||
}
|
|
||||||
// 执行查询
|
// 执行查询
|
||||||
const res = await AfterSaleApi.getAfterSalePage(data)
|
const res = await AfterSaleApi.getAfterSalePage(data)
|
||||||
list.value = res.list
|
list.value = res.list as AfterSaleApi.TradeAfterSaleVO[]
|
||||||
total.value = res.total
|
total.value = res.total
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
const handleQuery = async () => {
|
const handleQuery = async () => {
|
||||||
queryParams.pageNo = 1
|
queryParams.pageNo = 1
|
||||||
await getList()
|
await getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
/** 重置按钮操作 */
|
||||||
const resetQuery = () => {
|
const resetQuery = () => {
|
||||||
queryFormRef.value?.resetFields()
|
queryFormRef.value?.resetFields()
|
||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** tab 切换 */
|
/** tab 切换 */
|
||||||
const tabClick = async (tab: TabsPaneContext) => {
|
const tabClick = async (tab: TabsPaneContext) => {
|
||||||
queryParams.status = tab.paneName
|
queryParams.status = tab.paneName
|
||||||
|
@ -2,81 +2,57 @@
|
|||||||
<el-descriptions :column="2">
|
<el-descriptions :column="2">
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 等级 " icon="svg-icon:member_level" />
|
<descriptions-item-label icon="svg-icon:member_level" label=" 等级 " />
|
||||||
</template>
|
</template>
|
||||||
{{ user.levelName || '无' }}
|
{{ user.levelName || '无' }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 成长值 " icon="ep:suitcase" />
|
<descriptions-item-label icon="ep:suitcase" label=" 成长值 " />
|
||||||
</template>
|
</template>
|
||||||
{{ user.experience || 0 }}
|
{{ user.experience || 0 }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 当前积分 " icon="ep:coin" />
|
<descriptions-item-label icon="ep:coin" label=" 当前积分 " />
|
||||||
</template>
|
</template>
|
||||||
{{ user.point || 0 }}
|
{{ user.point || 0 }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 总积分 " icon="ep:coin" />
|
<descriptions-item-label icon="ep:coin" label=" 总积分 " />
|
||||||
</template>
|
</template>
|
||||||
{{ user.totalPoint || 0 }}
|
{{ user.totalPoint || 0 }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 当前余额 " icon="svg-icon:member_balance" />
|
<descriptions-item-label icon="svg-icon:member_balance" label=" 当前余额 " />
|
||||||
</template>
|
</template>
|
||||||
{{ fenToYuan(wallet.balance || 0) }}
|
{{ fenToYuan(wallet.balance || 0) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 支出金额 " icon="svg-icon:member_expenditure_balance" />
|
<descriptions-item-label icon="svg-icon:member_expenditure_balance" label=" 支出金额 " />
|
||||||
</template>
|
</template>
|
||||||
{{ fenToYuan(wallet.totalExpense || 0) }}
|
{{ fenToYuan(wallet.totalExpense || 0) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item>
|
<el-descriptions-item>
|
||||||
<template #label>
|
<template #label>
|
||||||
<descriptions-item-label label=" 充值金额 " icon="svg-icon:member_recharge_balance" />
|
<descriptions-item-label icon="svg-icon:member_recharge_balance" label=" 充值金额 " />
|
||||||
</template>
|
</template>
|
||||||
{{ fenToYuan(wallet.totalRecharge || 0) }}
|
{{ fenToYuan(wallet.totalRecharge || 0) }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script lang="ts" setup>
|
||||||
import { DescriptionsItemLabel } from '@/components/Descriptions'
|
import { DescriptionsItemLabel } from '@/components/Descriptions'
|
||||||
import * as UserApi from '@/api/member/user'
|
import * as UserApi from '@/api/member/user'
|
||||||
import * as WalletApi from '@/api/pay/wallet/balance'
|
import * as WalletApi from '@/api/pay/wallet/balance'
|
||||||
import { UserTypeEnum } from '@/utils/constants'
|
|
||||||
import { fenToYuan } from '@/utils'
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
const props = defineProps<{ user: UserApi.UserVO }>() // 用户信息
|
defineProps<{ user: UserApi.UserVO; wallet: WalletApi.WalletVO }>() // 用户信息
|
||||||
const WALLET_INIT_DATA = {
|
|
||||||
balance: 0,
|
|
||||||
totalExpense: 0,
|
|
||||||
totalRecharge: 0
|
|
||||||
} as WalletApi.WalletVO // 钱包初始化数据
|
|
||||||
const wallet = ref<WalletApi.WalletVO>(WALLET_INIT_DATA) // 钱包信息
|
|
||||||
|
|
||||||
/** 查询用户钱包信息 */
|
|
||||||
const getUserWallet = async () => {
|
|
||||||
if (!props.user.id) {
|
|
||||||
wallet.value = WALLET_INIT_DATA
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const params = { userId: props.user.id }
|
|
||||||
wallet.value = (await WalletApi.getWallet(params)) || WALLET_INIT_DATA
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听用户编号变化 */
|
|
||||||
watch(
|
|
||||||
() => props.user.id,
|
|
||||||
() => getUserWallet(),
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style lang="scss" scoped>
|
||||||
.cell-item {
|
.cell-item {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
276
src/views/member/user/detail/UserAftersaleList.vue
Normal file
276
src/views/member/user/detail/UserAftersaleList.vue
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 搜索 -->
|
||||||
|
<ContentWrap>
|
||||||
|
<el-form ref="queryFormRef" :inline="true" :model="queryParams" label-width="68px">
|
||||||
|
<el-form-item label="商品名称" prop="spuName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.spuName"
|
||||||
|
class="!w-280px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入商品 SPU 名称"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="退款编号" prop="no">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.no"
|
||||||
|
class="!w-280px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入退款编号"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="订单编号" prop="orderNo">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.orderNo"
|
||||||
|
class="!w-280px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入订单编号"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="售后状态" prop="status">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.status"
|
||||||
|
class="!w-280px"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择售后状态"
|
||||||
|
>
|
||||||
|
<el-option label="全部" value="0" />
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_STATUS)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="售后方式" prop="way">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.way"
|
||||||
|
class="!w-280px"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择售后方式"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_WAY)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="售后类型" prop="type">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.type"
|
||||||
|
class="!w-280px"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择售后类型"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_TYPE)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="创建时间" prop="createTime">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.createTime"
|
||||||
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
|
class="!w-260px"
|
||||||
|
end-placeholder="自定义时间"
|
||||||
|
start-placeholder="自定义时间"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button @click="handleQuery">
|
||||||
|
<Icon class="mr-5px" icon="ep:search" />
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="resetQuery">
|
||||||
|
<Icon class="mr-5px" icon="ep:refresh" />
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<ContentWrap>
|
||||||
|
<el-tabs v-model="queryParams.status" @tab-click="tabClick">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="item in statusTabs"
|
||||||
|
:key="item.label"
|
||||||
|
:label="item.label"
|
||||||
|
:name="item.value"
|
||||||
|
/>
|
||||||
|
</el-tabs>
|
||||||
|
<!-- 列表 -->
|
||||||
|
<el-table v-loading="loading" :data="list">
|
||||||
|
<el-table-column align="center" label="退款编号" min-width="200" prop="no" />
|
||||||
|
<el-table-column align="center" label="订单编号" min-width="200" prop="orderNo">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="openOrderDetail(row.orderId)">
|
||||||
|
{{ row.orderNo }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="商品信息" min-width="600" prop="spuName">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<el-image
|
||||||
|
:src="row.picUrl"
|
||||||
|
class="mr-10px h-30px w-30px"
|
||||||
|
@click="imagePreview(row.picUrl)"
|
||||||
|
/>
|
||||||
|
<span class="mr-10px">{{ row.spuName }}</span>
|
||||||
|
<el-tag v-for="property in row.properties" :key="property.propertyId" class="mr-10px">
|
||||||
|
{{ property.propertyName }}: {{ property.valueName }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="订单金额" min-width="120" prop="refundPrice">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ fenToYuan(scope.row.refundPrice) }} 元</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="申请时间" prop="createTime" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ formatDate(scope.row.createTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="售后状态" width="100">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="售后方式">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" fixed="right" label="操作" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="openAfterSaleDetail(row.id)">处理退款</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import * as AfterSaleApi from '@/api/mall/trade/afterSale/index'
|
||||||
|
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
||||||
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
|
import { TabsPaneContext } from 'element-plus'
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
|
defineOptions({ name: 'UserAfterSaleList' })
|
||||||
|
|
||||||
|
const { push } = useRouter() // 路由跳转
|
||||||
|
const props = defineProps<{
|
||||||
|
userId: number
|
||||||
|
}>()
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const list = ref<AfterSaleApi.TradeAfterSaleVO[]>([]) // 列表的数据
|
||||||
|
const statusTabs = ref([
|
||||||
|
{
|
||||||
|
label: '全部',
|
||||||
|
value: '0'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
const queryFormRef = ref() // 搜索的表单
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = ref({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
no: null,
|
||||||
|
status: '0',
|
||||||
|
orderNo: null,
|
||||||
|
spuName: null,
|
||||||
|
createTime: [],
|
||||||
|
way: null,
|
||||||
|
type: null,
|
||||||
|
userId: null
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = cloneDeep(queryParams.value)
|
||||||
|
// 处理掉全部的状态,不传就是全部
|
||||||
|
if (data.status === '0') {
|
||||||
|
delete data.status
|
||||||
|
}
|
||||||
|
// 执行查询
|
||||||
|
if (props.userId) {
|
||||||
|
data.userId = props.userId as any
|
||||||
|
}
|
||||||
|
const res = await AfterSaleApi.getAfterSalePage(data)
|
||||||
|
list.value = res.list as AfterSaleApi.TradeAfterSaleVO[]
|
||||||
|
total.value = res.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
const handleQuery = async () => {
|
||||||
|
queryParams.value.pageNo = 1
|
||||||
|
await getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
const resetQuery = () => {
|
||||||
|
queryFormRef.value?.resetFields()
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** tab 切换 */
|
||||||
|
const tabClick = async (tab: TabsPaneContext) => {
|
||||||
|
queryParams.value.status = tab.paneName as any
|
||||||
|
await getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 处理退款 */
|
||||||
|
const openAfterSaleDetail = (id: number) => {
|
||||||
|
push({ name: 'TradeAfterSaleDetail', params: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查看订单详情 */
|
||||||
|
const openOrderDetail = (id: number) => {
|
||||||
|
push({ name: 'TradeOrderDetail', params: { id } })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商品图预览 */
|
||||||
|
const imagePreview = (imgUrl: string) => {
|
||||||
|
createImageViewer({
|
||||||
|
urlList: [imgUrl]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await getList()
|
||||||
|
// 设置 statuses 过滤
|
||||||
|
for (const dict of getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_STATUS)) {
|
||||||
|
statusTabs.value.push({
|
||||||
|
label: dict.label,
|
||||||
|
value: dict.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
68
src/views/member/user/detail/UserBalanceList.vue
Normal file
68
src/views/member/user/detail/UserBalanceList.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<template>
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list" :show-overflow-tooltip="true" :stripe="true">
|
||||||
|
<el-table-column align="center" label="编号" prop="id" />
|
||||||
|
<el-table-column align="center" label="钱包编号" prop="walletId" />
|
||||||
|
<el-table-column align="center" label="关联业务标题" prop="title" />
|
||||||
|
<el-table-column align="center" label="交易金额" prop="price">
|
||||||
|
<template #default="{ row }"> {{ fenToYuan(row.price) }} 元</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column align="center" label="钱包余额" prop="balance">
|
||||||
|
<template #default="{ row }"> {{ fenToYuan(row.balance) }} 元</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:formatter="dateFormatter"
|
||||||
|
align="center"
|
||||||
|
label="交易时间"
|
||||||
|
prop="createTime"
|
||||||
|
width="180px"
|
||||||
|
/>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
import * as WalletTransactionApi from '@/api/pay/wallet/transaction'
|
||||||
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
|
defineOptions({ name: 'UserBalanceList' })
|
||||||
|
const props = defineProps({
|
||||||
|
walletId: {
|
||||||
|
type: Number,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
walletId: null
|
||||||
|
})
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
/** 查询列表 */
|
||||||
|
const getList = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
queryParams.walletId = props.walletId as any
|
||||||
|
const data = await WalletTransactionApi.getWalletTransactionPage(queryParams)
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
@ -20,7 +20,7 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<CardTitle title="账户信息" />
|
<CardTitle title="账户信息" />
|
||||||
</template>
|
</template>
|
||||||
<UserAccountInfo :user="user" />
|
<UserAccountInfo :user="user" :wallet="wallet" />
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<!-- 下边:账户明细 -->
|
<!-- 下边:账户明细 -->
|
||||||
@ -40,7 +40,7 @@
|
|||||||
<UserExperienceRecordList :user-id="id" />
|
<UserExperienceRecordList :user-id="id" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="余额" lazy>
|
<el-tab-pane label="余额" lazy>
|
||||||
<WalletTransactionList :user-id="id" />
|
<UserBalanceList :wallet-id="wallet.id" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="收货地址" lazy>
|
<el-tab-pane label="收货地址" lazy>
|
||||||
<UserAddressList :user-id="id" />
|
<UserAddressList :user-id="id" />
|
||||||
@ -49,7 +49,7 @@
|
|||||||
<UserOrderList :user-id="id" />
|
<UserOrderList :user-id="id" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="售后管理" lazy>
|
<el-tab-pane label="售后管理" lazy>
|
||||||
<TradeAfterSale :user-id="id" />
|
<UserAfterSaleList :user-id="id" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane label="收藏记录" lazy>
|
<el-tab-pane label="收藏记录" lazy>
|
||||||
<UserFavoriteList :user-id="id" />
|
<UserFavoriteList :user-id="id" />
|
||||||
@ -69,6 +69,7 @@
|
|||||||
<UserForm ref="formRef" @success="getUserData(id)" />
|
<UserForm ref="formRef" @success="getUserData(id)" />
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import * as WalletApi from '@/api/pay/wallet/balance'
|
||||||
import * as UserApi from '@/api/member/user'
|
import * as UserApi from '@/api/member/user'
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||||
import UserForm from '@/views/member/user/UserForm.vue'
|
import UserForm from '@/views/member/user/UserForm.vue'
|
||||||
@ -82,8 +83,8 @@ import UserOrderList from './UserOrderList.vue'
|
|||||||
import UserPointList from './UserPointList.vue'
|
import UserPointList from './UserPointList.vue'
|
||||||
import UserSignList from './UserSignList.vue'
|
import UserSignList from './UserSignList.vue'
|
||||||
import UserFavoriteList from './UserFavoriteList.vue'
|
import UserFavoriteList from './UserFavoriteList.vue'
|
||||||
import WalletTransactionList from '@/views/pay/wallet/transaction/WalletTransactionList.vue'
|
import UserAfterSaleList from './UserAftersaleList.vue'
|
||||||
import TradeAfterSale from '@/views/mall/trade/afterSale/index.vue'
|
import UserBalanceList from './UserBalanceList.vue'
|
||||||
import { CardTitle } from '@/components/Card/index'
|
import { CardTitle } from '@/components/Card/index'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
@ -113,6 +114,24 @@ const { currentRoute } = useRouter() // 路由
|
|||||||
const { delView } = useTagsViewStore() // 视图操作
|
const { delView } = useTagsViewStore() // 视图操作
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const id = Number(route.params.id)
|
const id = Number(route.params.id)
|
||||||
|
/* 用户钱包相关信息 */
|
||||||
|
const WALLET_INIT_DATA = {
|
||||||
|
balance: 0,
|
||||||
|
totalExpense: 0,
|
||||||
|
totalRecharge: 0
|
||||||
|
} as WalletApi.WalletVO // 钱包初始化数据
|
||||||
|
const wallet = ref<WalletApi.WalletVO>(WALLET_INIT_DATA) // 钱包信息
|
||||||
|
|
||||||
|
/** 查询用户钱包信息 */
|
||||||
|
const getUserWallet = async () => {
|
||||||
|
if (!id) {
|
||||||
|
wallet.value = WALLET_INIT_DATA
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = { userId: id }
|
||||||
|
wallet.value = (await WalletApi.getWallet(params)) || WALLET_INIT_DATA
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
ElMessage.warning('参数错误,会员编号不能为空!')
|
ElMessage.warning('参数错误,会员编号不能为空!')
|
||||||
@ -120,6 +139,7 @@ onMounted(() => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
getUserData(id)
|
getUserData(id)
|
||||||
|
getUserWallet()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
<el-form-item label="应用名" prop="name">
|
<el-form-item label="应用名" prop="name">
|
||||||
<el-input v-model="formData.name" placeholder="请输入应用名" />
|
<el-input v-model="formData.name" placeholder="请输入应用名" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="应用标识" prop="name">
|
||||||
|
<el-input v-model="formData.appKey" placeholder="请输入应用标识" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="开启状态" prop="status">
|
<el-form-item label="开启状态" prop="status">
|
||||||
<el-radio-group v-model="formData.status">
|
<el-radio-group v-model="formData.status">
|
||||||
<el-radio
|
<el-radio
|
||||||
@ -55,16 +58,15 @@ const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
|||||||
const formData = ref({
|
const formData = ref({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
packageId: undefined,
|
appKey: undefined,
|
||||||
contactName: undefined,
|
status: CommonStatusEnum.ENABLE,
|
||||||
contactMobile: undefined,
|
remark: undefined,
|
||||||
accountCount: undefined,
|
orderNotifyUrl: undefined,
|
||||||
expireTime: undefined,
|
refundNotifyUrl: undefined
|
||||||
domain: undefined,
|
|
||||||
status: CommonStatusEnum.ENABLE
|
|
||||||
})
|
})
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
name: [{ required: true, message: '应用名不能为空', trigger: 'blur' }],
|
name: [{ required: true, message: '应用名不能为空', trigger: 'blur' }],
|
||||||
|
appKey: [{ required: true, message: '应用标识不能为空', trigger: 'blur' }],
|
||||||
status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }],
|
status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }],
|
||||||
orderNotifyUrl: [{ required: true, message: '支付结果的回调地址不能为空', trigger: 'blur' }],
|
orderNotifyUrl: [{ required: true, message: '支付结果的回调地址不能为空', trigger: 'blur' }],
|
||||||
refundNotifyUrl: [{ required: true, message: '退款结果的回调地址不能为空', trigger: 'blur' }]
|
refundNotifyUrl: [{ required: true, message: '退款结果的回调地址不能为空', trigger: 'blur' }]
|
||||||
@ -123,7 +125,8 @@ const resetForm = () => {
|
|||||||
status: CommonStatusEnum.ENABLE,
|
status: CommonStatusEnum.ENABLE,
|
||||||
remark: undefined,
|
remark: undefined,
|
||||||
orderNotifyUrl: undefined,
|
orderNotifyUrl: undefined,
|
||||||
refundNotifyUrl: undefined
|
refundNotifyUrl: undefined,
|
||||||
|
appKey: undefined
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" @closed="close" width="830px">
|
<Dialog v-model="dialogVisible" :title="dialogTitle" width="830px" @closed="close">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="formData"
|
|
||||||
:formRules="formRules"
|
|
||||||
label-width="100px"
|
|
||||||
v-loading="formLoading"
|
v-loading="formLoading"
|
||||||
|
:model="formData"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="100px"
|
||||||
>
|
>
|
||||||
<el-form-item label-width="180px" label="渠道费率" prop="feeRate">
|
<el-form-item label="渠道费率" label-width="180px" prop="feeRate">
|
||||||
<el-input v-model="formData.feeRate" placeholder="请输入渠道费率" clearable>
|
<el-input v-model="formData.feeRate" clearable placeholder="请输入渠道费率">
|
||||||
<template #append>%</template>
|
<template #append>%</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="开放平台 APPID" prop="config.appId">
|
<el-form-item label="开放平台 APPID" label-width="180px" prop="config.appId">
|
||||||
<el-input v-model="formData.config.appId" placeholder="请输入开放平台 APPID" clearable />
|
<el-input v-model="formData.config.appId" clearable placeholder="请输入开放平台 APPID" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="渠道状态" prop="status">
|
<el-form-item label="渠道状态" label-width="180px" prop="status">
|
||||||
<el-radio-group v-model="formData.status">
|
<el-radio-group v-model="formData.status">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
@ -27,7 +27,7 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="网关地址" prop="config.serverUrl">
|
<el-form-item label="网关地址" label-width="180px" prop="config.serverUrl">
|
||||||
<el-radio-group v-model="formData.config.serverUrl">
|
<el-radio-group v-model="formData.config.serverUrl">
|
||||||
<el-radio label="https://openapi.alipay.com/gateway.do">线上环境</el-radio>
|
<el-radio label="https://openapi.alipay.com/gateway.do">线上环境</el-radio>
|
||||||
<el-radio label="https://openapi-sandbox.dl.alipaydev.com/gateway.do">
|
<el-radio label="https://openapi-sandbox.dl.alipaydev.com/gateway.do">
|
||||||
@ -35,128 +35,148 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="算法类型" prop="config.signType">
|
<el-form-item label="算法类型" label-width="180px" prop="config.signType">
|
||||||
<el-radio-group v-model="formData.config.signType">
|
<el-radio-group v-model="formData.config.signType">
|
||||||
<el-radio key="RSA2" label="RSA2">RSA2</el-radio>
|
<el-radio key="RSA2" label="RSA2">RSA2</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="公钥类型" prop="config.mode">
|
<el-form-item label="公钥类型" label-width="180px" prop="config.mode">
|
||||||
<el-radio-group v-model="formData.config.mode">
|
<el-radio-group v-model="formData.config.mode">
|
||||||
<el-radio key="公钥模式" :label="1">公钥模式</el-radio>
|
<el-radio key="公钥模式" :label="1">公钥模式</el-radio>
|
||||||
<el-radio key="证书模式" :label="2">证书模式</el-radio>
|
<el-radio key="证书模式" :label="2">证书模式</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div v-if="formData.config.mode === 1">
|
<div v-if="formData.config.mode === 1">
|
||||||
<el-form-item label-width="180px" label="应用私钥" prop="config.privateKey">
|
<el-form-item label="应用私钥" label-width="180px" prop="config.privateKey">
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
|
||||||
v-model="formData.config.privateKey"
|
v-model="formData.config.privateKey"
|
||||||
placeholder="请输入应用私钥"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
clearable
|
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入应用私钥"
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="支付宝公钥" prop="config.alipayPublicKey">
|
<el-form-item label="支付宝公钥" label-width="180px" prop="config.alipayPublicKey">
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
|
||||||
v-model="formData.config.alipayPublicKey"
|
v-model="formData.config.alipayPublicKey"
|
||||||
placeholder="请输入支付宝公钥"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
clearable
|
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入支付宝公钥"
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="formData.config.mode === 2">
|
<div v-if="formData.config.mode === 2">
|
||||||
<el-form-item label-width="180px" label="应用私钥" prop="config.privateKey">
|
<el-form-item label="应用私钥" label-width="180px" prop="config.privateKey">
|
||||||
<el-input
|
<el-input
|
||||||
type="textarea"
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
|
||||||
v-model="formData.config.privateKey"
|
v-model="formData.config.privateKey"
|
||||||
placeholder="请输入应用私钥"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
clearable
|
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入应用私钥"
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="商户公钥应用证书" prop="config.appCertContent">
|
<el-form-item label="商户公钥应用证书" label-width="180px" prop="config.appCertContent">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.appCertContent"
|
v-model="formData.config.appCertContent"
|
||||||
type="textarea"
|
|
||||||
placeholder="请上传商户公钥应用证书"
|
|
||||||
readonly
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请上传商户公钥应用证书"
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="">
|
<el-form-item label="" label-width="180px">
|
||||||
<el-upload
|
<el-upload
|
||||||
action=""
|
|
||||||
ref="privateKeyContentFile"
|
ref="privateKeyContentFile"
|
||||||
:limit="1"
|
|
||||||
:accept="fileAccept"
|
:accept="fileAccept"
|
||||||
:http-request="appCertUpload"
|
|
||||||
:before-upload="fileBeforeUpload"
|
:before-upload="fileBeforeUpload"
|
||||||
|
:http-request="appCertUpload"
|
||||||
|
:limit="1"
|
||||||
|
action=""
|
||||||
>
|
>
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
<Icon icon="ep:upload" class="mr-5px" /> 点击上传
|
<Icon class="mr-5px" icon="ep:upload" />
|
||||||
|
点击上传
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label-width="180px"
|
|
||||||
label="支付宝公钥证书"
|
label="支付宝公钥证书"
|
||||||
|
label-width="180px"
|
||||||
prop="config.alipayPublicCertContent"
|
prop="config.alipayPublicCertContent"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.alipayPublicCertContent"
|
v-model="formData.config.alipayPublicCertContent"
|
||||||
type="textarea"
|
|
||||||
placeholder="请上传支付宝公钥证书"
|
|
||||||
readonly
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请上传支付宝公钥证书"
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="">
|
<el-form-item label="" label-width="180px">
|
||||||
<el-upload
|
<el-upload
|
||||||
ref="privateCertContentFile"
|
ref="privateCertContentFile"
|
||||||
action=""
|
|
||||||
:limit="1"
|
|
||||||
:accept="fileAccept"
|
:accept="fileAccept"
|
||||||
:before-upload="fileBeforeUpload"
|
:before-upload="fileBeforeUpload"
|
||||||
:http-request="alipayPublicCertUpload"
|
:http-request="alipayPublicCertUpload"
|
||||||
|
:limit="1"
|
||||||
|
action=""
|
||||||
>
|
>
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
<Icon icon="ep:upload" class="mr-5px" /> 点击上传
|
<Icon class="mr-5px" icon="ep:upload" />
|
||||||
|
点击上传
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="根证书" prop="config.rootCertContent">
|
<el-form-item label="根证书" label-width="180px" prop="config.rootCertContent">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.rootCertContent"
|
v-model="formData.config.rootCertContent"
|
||||||
type="textarea"
|
|
||||||
placeholder="请上传根证书"
|
|
||||||
readonly
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请上传根证书"
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="">
|
<el-form-item label="" label-width="180px">
|
||||||
<el-upload
|
<el-upload
|
||||||
ref="privateCertContentFile"
|
ref="privateCertContentFile"
|
||||||
:limit="1"
|
|
||||||
:accept="fileAccept"
|
:accept="fileAccept"
|
||||||
action=""
|
|
||||||
:before-upload="fileBeforeUpload"
|
:before-upload="fileBeforeUpload"
|
||||||
:http-request="rootCertUpload"
|
:http-request="rootCertUpload"
|
||||||
|
:limit="1"
|
||||||
|
action=""
|
||||||
>
|
>
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
<Icon icon="ep:upload" class="mr-5px" /> 点击上传
|
<Icon class="mr-5px" icon="ep:upload" />
|
||||||
|
点击上传
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label-width="180px" label="备注" prop="remark">
|
|
||||||
|
<el-form-item label="接口内容加密方式" label-width="180px" prop="config.encryptType">
|
||||||
|
<el-radio-group v-model="formData.config.encryptType">
|
||||||
|
<el-radio key="NONE" label="">无加密</el-radio>
|
||||||
|
<el-radio key="AES" label="AES">AES</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<div v-if="formData.config.encryptType === 'AES'">
|
||||||
|
<el-form-item label="接口内容加密密钥" label-width="180px" prop="config.encryptKey">
|
||||||
|
<el-input
|
||||||
|
v-model="formData.config.encryptKey"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入接口内容加密密钥"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -195,7 +215,9 @@ const formData = ref<any>({
|
|||||||
alipayPublicKey: '',
|
alipayPublicKey: '',
|
||||||
appCertContent: '',
|
appCertContent: '',
|
||||||
alipayPublicCertContent: '',
|
alipayPublicCertContent: '',
|
||||||
rootCertContent: ''
|
rootCertContent: '',
|
||||||
|
encryptType: '',
|
||||||
|
encryptKey: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const formRules = {
|
const formRules = {
|
||||||
@ -213,7 +235,8 @@ const formRules = {
|
|||||||
'config.alipayPublicCertContent': [
|
'config.alipayPublicCertContent': [
|
||||||
{ required: true, message: '请上传支付宝公钥证书', trigger: 'blur' }
|
{ required: true, message: '请上传支付宝公钥证书', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
'config.rootCertContent': [{ required: true, message: '请上传指定根证书', trigger: 'blur' }]
|
'config.rootCertContent': [{ required: true, message: '请上传指定根证书', trigger: 'blur' }],
|
||||||
|
'config.encryptKey': [{ required: true, message: '请输入接口内容加密密钥', trigger: 'blur' }]
|
||||||
}
|
}
|
||||||
const fileAccept = '.crt'
|
const fileAccept = '.crt'
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
@ -281,7 +304,9 @@ const resetForm = (appId, code) => {
|
|||||||
alipayPublicKey: '',
|
alipayPublicKey: '',
|
||||||
appCertContent: '',
|
appCertContent: '',
|
||||||
alipayPublicCertContent: '',
|
alipayPublicCertContent: '',
|
||||||
rootCertContent: ''
|
rootCertContent: '',
|
||||||
|
encryptType: '',
|
||||||
|
encryptKey: ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" @closed="close" width="800px">
|
<Dialog v-model="dialogVisible" :title="dialogTitle" width="800px">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
|
v-loading="formLoading"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
>
|
||||||
<el-form-item label-width="180px" label="渠道状态" prop="status">
|
<el-form-item label="渠道状态" label-width="180px" prop="status">
|
||||||
<el-radio-group v-model="formData.status">
|
<el-radio-group v-model="formData.status">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="备注" prop="remark">
|
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" @closed="close" width="800px">
|
<Dialog v-model="dialogVisible" :title="dialogTitle" width="800px">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
|
v-loading="formLoading"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
>
|
||||||
<el-form-item label-width="180px" label="渠道状态" prop="status">
|
<el-form-item label="渠道状态" label-width="180px" prop="status">
|
||||||
<el-radio-group v-model="formData.status">
|
<el-radio-group v-model="formData.status">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
@ -19,7 +19,7 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="备注" prop="remark">
|
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
@ -1,35 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Dialog v-model="dialogVisible" :title="dialogTitle" @close="close" width="800px">
|
<Dialog v-model="dialogVisible" :title="dialogTitle" width="800px">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
|
v-loading="formLoading"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="120px"
|
label-width="120px"
|
||||||
v-loading="formLoading"
|
|
||||||
>
|
>
|
||||||
<el-form-item label-width="180px" label="渠道费率" prop="feeRate">
|
<el-form-item label="渠道费率" label-width="180px" prop="feeRate">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.feeRate"
|
v-model="formData.feeRate"
|
||||||
placeholder="请输入渠道费率"
|
|
||||||
clearable
|
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入渠道费率"
|
||||||
>
|
>
|
||||||
<template #append>%</template>
|
<template #append>%</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="微信 APPID" prop="config.appId">
|
<el-form-item label="微信 APPID" label-width="180px" prop="config.appId">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.appId"
|
v-model="formData.config.appId"
|
||||||
placeholder="请输入微信 APPID"
|
|
||||||
clearable
|
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入微信 APPID"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="商户号" prop="config.mchId">
|
<el-form-item label="商户号" label-width="180px" prop="config.mchId">
|
||||||
<el-input v-model="formData.config.mchId" :style="{ width: '100%' }" />
|
<el-input v-model="formData.config.mchId" :style="{ width: '100%' }" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="渠道状态" prop="status">
|
<el-form-item label="渠道状态" label-width="180px" prop="status">
|
||||||
<el-radio-group v-model="formData.status">
|
<el-radio-group v-model="formData.status">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
v-for="dict in getDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
@ -40,95 +40,91 @@
|
|||||||
</el-radio>
|
</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="API 版本" prop="config.apiVersion">
|
<el-form-item label="API 版本" label-width="180px" prop="config.apiVersion">
|
||||||
<el-radio-group v-model="formData.config.apiVersion">
|
<el-radio-group v-model="formData.config.apiVersion">
|
||||||
<el-radio label="v2">v2</el-radio>
|
<el-radio label="v2">v2</el-radio>
|
||||||
<el-radio label="v3">v3</el-radio>
|
<el-radio label="v3">v3</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<div v-if="formData.config.apiVersion === 'v2'">
|
<div v-if="formData.config.apiVersion === 'v2'">
|
||||||
<el-form-item label-width="180px" label="商户密钥" prop="config.mchKey">
|
<el-form-item label="商户密钥" label-width="180px" prop="config.mchKey">
|
||||||
<el-input
|
<el-input v-model="formData.config.mchKey" clearable placeholder="请输入商户密钥" />
|
||||||
v-model="formData.config.mchKey"
|
|
||||||
placeholder="请输入商户密钥"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label-width="180px"
|
|
||||||
label="apiclient_cert.p12 证书"
|
label="apiclient_cert.p12 证书"
|
||||||
|
label-width="180px"
|
||||||
prop="config.keyContent"
|
prop="config.keyContent"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.keyContent"
|
v-model="formData.config.keyContent"
|
||||||
type="textarea"
|
|
||||||
placeholder="请上传 apiclient_cert.p12 证书"
|
|
||||||
readonly
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请上传 apiclient_cert.p12 证书"
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="">
|
<el-form-item label="" label-width="180px">
|
||||||
<el-upload
|
<el-upload
|
||||||
|
:before-upload="p12FileBeforeUpload"
|
||||||
|
:http-request="keyContentUpload"
|
||||||
:limit="1"
|
:limit="1"
|
||||||
accept=".p12"
|
accept=".p12"
|
||||||
action=""
|
action=""
|
||||||
:before-upload="p12FileBeforeUpload"
|
|
||||||
:http-request="keyContentUpload"
|
|
||||||
>
|
>
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
<Icon icon="ep:upload" class="mr-5px" />
|
<Icon class="mr-5px" icon="ep:upload" />
|
||||||
点击上传
|
点击上传
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="formData.config.apiVersion === 'v3'">
|
<div v-if="formData.config.apiVersion === 'v3'">
|
||||||
<el-form-item label-width="180px" label="API V3 密钥" prop="config.apiV3Key">
|
<el-form-item label="API V3 密钥" label-width="180px" prop="config.apiV3Key">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.apiV3Key"
|
v-model="formData.config.apiV3Key"
|
||||||
placeholder="请输入 API V3 密钥"
|
|
||||||
clearable
|
clearable
|
||||||
|
placeholder="请输入 API V3 密钥"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label-width="180px"
|
|
||||||
label="apiclient_key.pem 证书"
|
label="apiclient_key.pem 证书"
|
||||||
|
label-width="180px"
|
||||||
prop="config.privateKeyContent"
|
prop="config.privateKeyContent"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.privateKeyContent"
|
v-model="formData.config.privateKeyContent"
|
||||||
type="textarea"
|
|
||||||
placeholder="请上传 apiclient_key.pem 证书"
|
|
||||||
readonly
|
|
||||||
:autosize="{ minRows: 8, maxRows: 8 }"
|
:autosize="{ minRows: 8, maxRows: 8 }"
|
||||||
:style="{ width: '100%' }"
|
:style="{ width: '100%' }"
|
||||||
|
placeholder="请上传 apiclient_key.pem 证书"
|
||||||
|
readonly
|
||||||
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="" prop="privateKeyContentFile">
|
<el-form-item label="" label-width="180px" prop="privateKeyContentFile">
|
||||||
<el-upload
|
<el-upload
|
||||||
ref="privateKeyContentFile"
|
ref="privateKeyContentFile"
|
||||||
|
:before-upload="pemFileBeforeUpload"
|
||||||
|
:http-request="privateKeyContentUpload"
|
||||||
:limit="1"
|
:limit="1"
|
||||||
accept=".pem"
|
accept=".pem"
|
||||||
action=""
|
action=""
|
||||||
:before-upload="pemFileBeforeUpload"
|
|
||||||
:http-request="privateKeyContentUpload"
|
|
||||||
>
|
>
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
<Icon icon="ep:upload" class="mr-5px" />
|
<Icon class="mr-5px" icon="ep:upload" />
|
||||||
点击上传
|
点击上传
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label-width="180px" label="证书序列号" prop="config.certSerialNo">
|
<el-form-item label="证书序列号" label-width="180px" prop="config.certSerialNo">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="formData.config.certSerialNo"
|
v-model="formData.config.certSerialNo"
|
||||||
placeholder="请输入证书序列号"
|
|
||||||
clearable
|
clearable
|
||||||
|
placeholder="请输入证书序列号"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</div>
|
</div>
|
||||||
<el-form-item label-width="180px" label="备注" prop="remark">
|
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@ -182,9 +178,7 @@ const formRules = {
|
|||||||
'config.privateKeyContent': [
|
'config.privateKeyContent': [
|
||||||
{ required: true, message: '请上传 apiclient_key.pem 证书', trigger: 'blur' }
|
{ required: true, message: '请上传 apiclient_key.pem 证书', trigger: 'blur' }
|
||||||
],
|
],
|
||||||
'config.certSerialNo': [
|
'config.certSerialNo': [{ required: true, message: '请输入证书序列号', trigger: 'blur' }],
|
||||||
{ required: true, message: '请输入证书序列号', trigger: 'blur' }
|
|
||||||
],
|
|
||||||
'config.apiV3Key': [{ required: true, message: '请上传 api V3 密钥值', trigger: 'blur' }]
|
'config.apiV3Key': [{ required: true, message: '请上传 api V3 密钥值', trigger: 'blur' }]
|
||||||
}
|
}
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
|
@ -3,27 +3,27 @@
|
|||||||
<!-- 搜索 -->
|
<!-- 搜索 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-form
|
<el-form
|
||||||
class="-mb-15px"
|
|
||||||
:model="queryParams"
|
|
||||||
ref="queryFormRef"
|
ref="queryFormRef"
|
||||||
:inline="true"
|
:inline="true"
|
||||||
|
:model="queryParams"
|
||||||
|
class="-mb-15px"
|
||||||
label-width="68px"
|
label-width="68px"
|
||||||
>
|
>
|
||||||
<el-form-item label="应用名" prop="name">
|
<el-form-item label="应用名" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.name"
|
v-model="queryParams.name"
|
||||||
placeholder="请输入应用名"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入应用名"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="开启状态" prop="status">
|
<el-form-item label="开启状态" prop="status">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.status"
|
v-model="queryParams.status"
|
||||||
placeholder="请选择开启状态"
|
|
||||||
clearable
|
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
|
clearable
|
||||||
|
placeholder="请选择开启状态"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
|
||||||
@ -36,25 +36,25 @@
|
|||||||
<el-form-item label="创建时间" prop="createTime">
|
<el-form-item label="创建时间" prop="createTime">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="queryParams.createTime"
|
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')]"
|
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
type="daterange"
|
||||||
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button @click="handleQuery">
|
<el-button @click="handleQuery">
|
||||||
<Icon icon="ep:search" class="mr-5px" />
|
<Icon class="mr-5px" icon="ep:search" />
|
||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button @click="resetQuery">
|
<el-button @click="resetQuery">
|
||||||
<Icon icon="ep:refresh" class="mr-5px" />
|
<Icon class="mr-5px" icon="ep:refresh" />
|
||||||
重置
|
重置
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['pay:app:create']">
|
<el-button v-hasPermi="['pay:app:create']" plain type="primary" @click="openForm('create')">
|
||||||
<Icon icon="ep:plus" class="mr-5px" />
|
<Icon class="mr-5px" icon="ep:plus" />
|
||||||
新增
|
新增
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -64,9 +64,9 @@
|
|||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list">
|
<el-table v-loading="loading" :data="list">
|
||||||
<el-table-column label="应用编号" align="center" prop="id" />
|
<el-table-column align="center" label="应用标识" prop="appKey" />
|
||||||
<el-table-column label="应用名" align="center" prop="name" />
|
<el-table-column align="center" label="应用名" min-width="90" prop="name" />
|
||||||
<el-table-column label="开启状态" align="center" prop="status">
|
<el-table-column align="center" label="开启状态" prop="status">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="scope.row.status"
|
v-model="scope.row.status"
|
||||||
@ -76,26 +76,28 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="支付宝配置" align="center">
|
<el-table-column align="center" label="支付宝配置">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="channel.name"
|
|
||||||
align="center"
|
|
||||||
v-for="channel in alipayChannels"
|
v-for="channel in alipayChannels"
|
||||||
:key="channel.code"
|
:key="channel.code"
|
||||||
|
:label="channel.name.replace('支付宝', '')"
|
||||||
|
align="center"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="success"
|
|
||||||
v-if="isChannelExists(scope.row.channelCodes, channel.code)"
|
v-if="isChannelExists(scope.row.channelCodes, channel.code)"
|
||||||
@click="openChannelForm(scope.row, channel.code)"
|
|
||||||
circle
|
circle
|
||||||
|
size="small"
|
||||||
|
type="success"
|
||||||
|
@click="openChannelForm(scope.row, channel.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:check" />
|
<Icon icon="ep:check" />
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="danger"
|
|
||||||
circle
|
circle
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
@click="openChannelForm(scope.row, channel.code)"
|
@click="openChannelForm(scope.row, channel.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:close" />
|
<Icon icon="ep:close" />
|
||||||
@ -103,26 +105,28 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="微信配置" align="center">
|
<el-table-column align="center" label="微信配置">
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="channel.name"
|
|
||||||
align="center"
|
|
||||||
v-for="channel in wxChannels"
|
v-for="channel in wxChannels"
|
||||||
:key="channel.code"
|
:key="channel.code"
|
||||||
|
:label="channel.name.replace('微信', '')"
|
||||||
|
align="center"
|
||||||
>
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="success"
|
|
||||||
v-if="isChannelExists(scope.row.channelCodes, channel.code)"
|
v-if="isChannelExists(scope.row.channelCodes, channel.code)"
|
||||||
@click="openChannelForm(scope.row, channel.code)"
|
|
||||||
circle
|
circle
|
||||||
|
size="small"
|
||||||
|
type="success"
|
||||||
|
@click="openChannelForm(scope.row, channel.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:check" />
|
<Icon icon="ep:check" />
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="danger"
|
|
||||||
circle
|
circle
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
@click="openChannelForm(scope.row, channel.code)"
|
@click="openChannelForm(scope.row, channel.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:close" />
|
<Icon icon="ep:close" />
|
||||||
@ -130,21 +134,23 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="钱包支付配置" align="center">
|
<el-table-column align="center" label="钱包支付配置">
|
||||||
<el-table-column :label="PayChannelEnum.WALLET.name" align="center">
|
<el-table-column :label="PayChannelEnum.WALLET.name" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="success"
|
|
||||||
circle
|
|
||||||
v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.WALLET.code)"
|
v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.WALLET.code)"
|
||||||
|
circle
|
||||||
|
size="small"
|
||||||
|
type="success"
|
||||||
@click="openChannelForm(scope.row, PayChannelEnum.WALLET.code)"
|
@click="openChannelForm(scope.row, PayChannelEnum.WALLET.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:check" />
|
<Icon icon="ep:check" />
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="danger"
|
|
||||||
circle
|
circle
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
@click="openChannelForm(scope.row, PayChannelEnum.WALLET.code)"
|
@click="openChannelForm(scope.row, PayChannelEnum.WALLET.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:close" />
|
<Icon icon="ep:close" />
|
||||||
@ -152,21 +158,23 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="模拟支付配置" align="center">
|
<el-table-column align="center" label="模拟支付配置">
|
||||||
<el-table-column :label="PayChannelEnum.MOCK.name" align="center">
|
<el-table-column :label="PayChannelEnum.MOCK.name" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
type="success"
|
|
||||||
circle
|
|
||||||
v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.MOCK.code)"
|
v-if="isChannelExists(scope.row.channelCodes, PayChannelEnum.MOCK.code)"
|
||||||
|
circle
|
||||||
|
size="small"
|
||||||
|
type="success"
|
||||||
@click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)"
|
@click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:check" />
|
<Icon icon="ep:check" />
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-else
|
v-else
|
||||||
type="danger"
|
|
||||||
circle
|
circle
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
@click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)"
|
@click="openChannelForm(scope.row, PayChannelEnum.MOCK.code)"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:close" />
|
<Icon icon="ep:close" />
|
||||||
@ -174,21 +182,21 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" align="center" min-width="110" fixed="right">
|
<el-table-column align="center" fixed="right" label="操作" min-width="110">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
|
v-hasPermi="['pay:app:update']"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openForm('update', scope.row.id)"
|
@click="openForm('update', scope.row.id)"
|
||||||
v-hasPermi="['pay:app:update']"
|
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
|
v-hasPermi="['pay:app:delete']"
|
||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
@click="handleDelete(scope.row.id)"
|
@click="handleDelete(scope.row.id)"
|
||||||
v-hasPermi="['pay:app:delete']"
|
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -197,9 +205,9 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<Pagination
|
<Pagination
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNo"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
:total="total"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
@ -255,7 +263,7 @@ const wxChannels = [
|
|||||||
PayChannelEnum.WX_APP,
|
PayChannelEnum.WX_APP,
|
||||||
PayChannelEnum.WX_NATIVE,
|
PayChannelEnum.WX_NATIVE,
|
||||||
PayChannelEnum.WX_WAP,
|
PayChannelEnum.WX_WAP,
|
||||||
PayChannelEnum.WX_BAR,
|
PayChannelEnum.WX_BAR
|
||||||
]
|
]
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
|
@ -13,19 +13,19 @@ module.exports = {
|
|||||||
'at-rule-no-unknown': [
|
'at-rule-no-unknown': [
|
||||||
true,
|
true,
|
||||||
{
|
{
|
||||||
ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin']
|
ignoreAtRules: ['function', 'if', 'each', 'include', 'mixin', 'extend']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
'media-query-no-invalid': null,
|
'media-query-no-invalid': null,
|
||||||
'function-no-unknown': null,
|
'function-no-unknown': null,
|
||||||
'no-empty-source': null,
|
'no-empty-source': null,
|
||||||
'named-grid-areas-no-invalid': null,
|
'named-grid-areas-no-invalid': null,
|
||||||
'unicode-bom': 'never',
|
// 'unicode-bom': 'never',
|
||||||
'no-descending-specificity': null,
|
'no-descending-specificity': null,
|
||||||
'font-family-no-missing-generic-family-keyword': null,
|
'font-family-no-missing-generic-family-keyword': null,
|
||||||
'declaration-colon-space-after': 'always-single-line',
|
// 'declaration-colon-space-after': 'always-single-line',
|
||||||
'declaration-colon-space-before': 'never',
|
// 'declaration-colon-space-before': 'never',
|
||||||
'declaration-block-trailing-semicolon': null,
|
// 'declaration-block-trailing-semicolon': null,
|
||||||
'rule-empty-line-before': [
|
'rule-empty-line-before': [
|
||||||
'always',
|
'always',
|
||||||
{
|
{
|
||||||
|
@ -24,11 +24,11 @@
|
|||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
},
|
},
|
||||||
"types": [
|
"types": [
|
||||||
"@intlify/unplugin-vue-i18n/types",
|
// "@intlify/unplugin-vue-i18n/types",
|
||||||
"vite/client",
|
"vite/client"
|
||||||
"element-plus/global",
|
// "element-plus/global",
|
||||||
"@types/qrcode",
|
// "@types/qrcode",
|
||||||
"vite-plugin-svg-icons/client"
|
// "vite-plugin-svg-icons/client"
|
||||||
],
|
],
|
||||||
"outDir": "target", // 请保留这个属性,防止tsconfig.json文件报错
|
"outDir": "target", // 请保留这个属性,防止tsconfig.json文件报错
|
||||||
"typeRoots": ["./node_modules/@types/", "./types"]
|
"typeRoots": ["./node_modules/@types/", "./types"]
|
||||||
|
143
vite.config.ts
143
vite.config.ts
@ -1,78 +1,85 @@
|
|||||||
import { resolve } from 'path'
|
import {resolve} from 'path'
|
||||||
import { loadEnv } from 'vite'
|
import type {ConfigEnv, UserConfig} from 'vite'
|
||||||
import type { UserConfig, ConfigEnv } from 'vite'
|
import {loadEnv} from 'vite'
|
||||||
import { createVitePlugins } from './build/vite'
|
import {createVitePlugins} from './build/vite'
|
||||||
import { include, exclude } from "./build/vite/optimize"
|
import {exclude, include} from "./build/vite/optimize"
|
||||||
// 当前执行node命令时文件夹的地址(工作目录)
|
// 当前执行node命令时文件夹的地址(工作目录)
|
||||||
const root = process.cwd()
|
const root = process.cwd()
|
||||||
|
|
||||||
// 路径查找
|
// 路径查找
|
||||||
function pathResolve(dir: string) {
|
function pathResolve(dir: string) {
|
||||||
return resolve(root, '.', dir)
|
return resolve(root, '.', dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default ({ command, mode }: ConfigEnv): UserConfig => {
|
export default ({command, mode}: ConfigEnv): UserConfig => {
|
||||||
let env = {} as any
|
let env = {} as any
|
||||||
const isBuild = command === 'build'
|
const isBuild = command === 'build'
|
||||||
if (!isBuild) {
|
if (!isBuild) {
|
||||||
env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
|
env = loadEnv((process.argv[3] === '--mode' ? process.argv[4] : process.argv[3]), root)
|
||||||
} else {
|
} else {
|
||||||
env = loadEnv(mode, root)
|
env = loadEnv(mode, root)
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
base: env.VITE_BASE_PATH,
|
base: env.VITE_BASE_PATH,
|
||||||
root: root,
|
root: root,
|
||||||
// 服务端渲染
|
// 服务端渲染
|
||||||
server: {
|
server: {
|
||||||
port: env.VITE_PORT, // 端口号
|
port: env.VITE_PORT, // 端口号
|
||||||
host: "0.0.0.0",
|
host: "0.0.0.0",
|
||||||
open: env.VITE_OPEN === 'true',
|
open: env.VITE_OPEN === 'true',
|
||||||
// 本地跨域代理. 目前注释的原因:暂时没有用途,server 端已经支持跨域
|
// 本地跨域代理. 目前注释的原因:暂时没有用途,server 端已经支持跨域
|
||||||
// proxy: {
|
// proxy: {
|
||||||
// ['/admin-api']: {
|
// ['/admin-api']: {
|
||||||
// target: env.VITE_BASE_URL,
|
// target: env.VITE_BASE_URL,
|
||||||
// ws: false,
|
// ws: false,
|
||||||
// changeOrigin: true,
|
// changeOrigin: true,
|
||||||
// rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),
|
// rewrite: (path) => path.replace(new RegExp(`^/admin-api`), ''),
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
},
|
|
||||||
// 项目使用的vite插件。 单独提取到build/vite/plugin中管理
|
|
||||||
plugins: createVitePlugins(),
|
|
||||||
css: {
|
|
||||||
preprocessorOptions: {
|
|
||||||
scss: {
|
|
||||||
additionalData: '@import "./src/styles/variables.scss";',
|
|
||||||
javascriptEnabled: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.scss', '.css'],
|
|
||||||
alias: [
|
|
||||||
{
|
|
||||||
find: 'vue-i18n',
|
|
||||||
replacement: 'vue-i18n/dist/vue-i18n.cjs.js'
|
|
||||||
},
|
},
|
||||||
{
|
// 项目使用的vite插件。 单独提取到build/vite/plugin中管理
|
||||||
find: /\@\//,
|
plugins: createVitePlugins(),
|
||||||
replacement: `${pathResolve('src')}/`
|
css: {
|
||||||
}
|
preprocessorOptions: {
|
||||||
]
|
scss: {
|
||||||
},
|
additionalData: '@import "./src/styles/variables.scss";',
|
||||||
build: {
|
javascriptEnabled: true
|
||||||
minify: 'terser',
|
}
|
||||||
outDir: env.VITE_OUT_DIR || 'dist',
|
}
|
||||||
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
|
},
|
||||||
// brotliSize: false,
|
resolve: {
|
||||||
terserOptions: {
|
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.scss', '.css'],
|
||||||
compress: {
|
alias: [
|
||||||
drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
|
{
|
||||||
drop_console: env.VITE_DROP_CONSOLE === 'true'
|
find: 'vue-i18n',
|
||||||
}
|
replacement: 'vue-i18n/dist/vue-i18n.cjs.js'
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
optimizeDeps: { include, exclude }
|
find: /\@\//,
|
||||||
}
|
replacement: `${pathResolve('src')}/`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
minify: 'terser',
|
||||||
|
outDir: env.VITE_OUT_DIR || 'dist',
|
||||||
|
sourcemap: env.VITE_SOURCEMAP === 'true' ? 'inline' : false,
|
||||||
|
// brotliSize: false,
|
||||||
|
terserOptions: {
|
||||||
|
compress: {
|
||||||
|
drop_debugger: env.VITE_DROP_DEBUGGER === 'true',
|
||||||
|
drop_console: env.VITE_DROP_CONSOLE === 'true'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
manualChunks: {
|
||||||
|
echarts: ['echarts'] // 将 echarts 单独打包,参考 https://gitee.com/yudaocode/yudao-ui-admin-vue3/issues/IAB1SX 讨论
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
optimizeDeps: {include, exclude}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user