feat(merchant): 添加平台费用字段并优化费率表编辑功能
- 在API模型和类型定义中新增platformFee字段 - 更新文档自动生成文件以包含新字段 - 重构费率表格组件,添加JSON批量编辑功能 - 实现表格与JSON数据的实时双向同步 - 添加还原默认数据按钮和相关逻辑 - 引入防抖函数优化JSON输入响应性能 - 添加数据验证确保输入格式正确性 - 更新表格列配置以显示商户加点字段 - 改进UI样式增加序列化编辑区域 - 添加新的lint脚本到本地设置配置
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
"mcp__mysql__execute",
|
||||
"Bash(pnpm type:check)",
|
||||
"Bash(npx eslint:*)",
|
||||
"mcp__ide__getDiagnostics"
|
||||
"mcp__ide__getDiagnostics",
|
||||
"Bash(pnpm lint:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
| **factLabel** | **number** | 实际面值 | [optional] [default to undefined] |
|
||||
| **showLabel** | **number** | 展示面额 | [optional] [default to undefined] |
|
||||
| **platformLabel** | **string** | 平台 | [optional] [default to undefined] |
|
||||
| **platformFee** | **number** | 平台费 | [optional] [default to undefined] |
|
||||
| **isLinkSingle** | **boolean** | 链接是否单独放置 | [optional] [default to undefined] |
|
||||
| **value** | **number** | 费率 | [optional] [default to undefined] |
|
||||
| **linkID** | **string** | 链接 | [optional] [default to undefined] |
|
||||
@@ -21,6 +22,7 @@ const instance: KamiApiMerchantV1PlatformRateRecord = {
|
||||
factLabel,
|
||||
showLabel,
|
||||
platformLabel,
|
||||
platformFee,
|
||||
isLinkSingle,
|
||||
value,
|
||||
linkID,
|
||||
|
||||
@@ -25,6 +25,10 @@ export interface KamiApiMerchantV1PlatformRateRecord {
|
||||
* 平台
|
||||
*/
|
||||
platformLabel?: string;
|
||||
/**
|
||||
* 平台费
|
||||
*/
|
||||
platformFee?: number;
|
||||
/**
|
||||
* 链接是否单独放置
|
||||
*/
|
||||
|
||||
@@ -9,6 +9,7 @@ export type merchantDeployRate = {
|
||||
isLinkSingle: boolean;
|
||||
value: number;
|
||||
linkID: string;
|
||||
platformFee: number;
|
||||
sort?: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
<template>
|
||||
<div class="rate-table-container">
|
||||
<div class="serialization-section">
|
||||
<a-space direction="vertical" style="width: 100%; margin-bottom: 16px">
|
||||
<a-typography-text type="secondary">
|
||||
批量编辑 (JSON格式) - 与表格实时同步
|
||||
</a-typography-text>
|
||||
<a-textarea
|
||||
v-model="serializationData"
|
||||
placeholder="在此编辑JSON数据,表格会实时同步更新"
|
||||
auto-size
|
||||
allow-clear
|
||||
/>
|
||||
<a-space>
|
||||
<a-button size="small" status="warning" @click="resetToDefault">
|
||||
<template #icon>
|
||||
<icon-refresh />
|
||||
</template>
|
||||
还原默认
|
||||
</a-button>
|
||||
</a-space>
|
||||
</a-space>
|
||||
</div>
|
||||
<a-table
|
||||
:data="tableData"
|
||||
:columns="columns"
|
||||
@@ -40,6 +62,13 @@
|
||||
<a-option :value="false" label="否" />
|
||||
</a-select>
|
||||
</template>
|
||||
<template #platformFee="{ rowIndex }">
|
||||
<a-input-number
|
||||
v-model="tableData[rowIndex].platformFee"
|
||||
:precision="2"
|
||||
required
|
||||
/>
|
||||
</template>
|
||||
<template #linkID="{ rowIndex }">
|
||||
<a-input v-model="tableData[rowIndex].linkID" required />
|
||||
</template>
|
||||
@@ -57,7 +86,11 @@
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-button size="small" status="danger" @click="deleteRecord(rowIndex)">
|
||||
<a-button
|
||||
size="small"
|
||||
status="danger"
|
||||
@click="deleteRecord(rowIndex)"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-delete />
|
||||
</template>
|
||||
@@ -65,6 +98,7 @@
|
||||
</a-space>
|
||||
</template>
|
||||
</a-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -73,7 +107,8 @@ import { openExternalLink } from './data';
|
||||
import { useClipboard } from '@vueuse/core';
|
||||
import { TableColumnData } from '@arco-design/web-vue/es/table';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { watch, watchEffect } from 'vue';
|
||||
import { watchEffect, ref, watch } from 'vue';
|
||||
import { useDebounceFn } from '@vueuse/core';
|
||||
|
||||
const generateTableData = () => {
|
||||
return {
|
||||
@@ -83,10 +118,12 @@ const generateTableData = () => {
|
||||
isLinkSingle: true,
|
||||
value: 0,
|
||||
sort: 0,
|
||||
platformFee: 0,
|
||||
linkID: ''
|
||||
};
|
||||
};
|
||||
const tableData = defineModel<merchantDeployRate[]>();
|
||||
const serializationData = ref('');
|
||||
|
||||
const { copy } = useClipboard();
|
||||
|
||||
@@ -121,6 +158,11 @@ const columns: TableColumnData[] = [
|
||||
dataIndex: 'value',
|
||||
slotName: 'value'
|
||||
},
|
||||
{
|
||||
title: '商户加点',
|
||||
dataIndex: 'platformFee',
|
||||
slotName: 'platformFee'
|
||||
},
|
||||
{
|
||||
title: '商品链接',
|
||||
dataIndex: 'linkID',
|
||||
@@ -143,6 +185,62 @@ watchEffect(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// 实时同步:tableData 变化时更新 serializationData
|
||||
watch(
|
||||
tableData,
|
||||
newData => {
|
||||
try {
|
||||
serializationData.value = JSON.stringify(newData, null, 2);
|
||||
} catch (error) {
|
||||
console.error('序列化失败:', error);
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
// 实时同步:serializationData 变化时更新 tableData
|
||||
const updateTableFromSerialization = useDebounceFn((newData: string) => {
|
||||
try {
|
||||
if (!newData?.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedData = JSON.parse(newData);
|
||||
|
||||
if (!Array.isArray(parsedData)) {
|
||||
throw new Error('数据格式必须是数组');
|
||||
}
|
||||
|
||||
// 验证每个记录的字段
|
||||
const validData = parsedData.map((item, index) => {
|
||||
if (typeof item !== 'object' || item === null) {
|
||||
throw new Error(`第${index + 1}条记录格式不正确`);
|
||||
}
|
||||
|
||||
return {
|
||||
factLabel: Number(item.factLabel) || 0,
|
||||
showLabel: Number(item.showLabel) || 0,
|
||||
platformLabel: String(item.platformLabel || ''),
|
||||
isLinkSingle: Boolean(item.isLinkSingle ?? true),
|
||||
value: Number(item.value) || 0,
|
||||
sort: Number(item.sort) || 0,
|
||||
platformFee: Number(item.platformFee) || 0,
|
||||
linkID: String(item.linkID || '')
|
||||
};
|
||||
});
|
||||
|
||||
// 避免循环更新,检查数据是否真的发生了变化
|
||||
if (JSON.stringify(validData) !== JSON.stringify(tableData.value)) {
|
||||
tableData.value = validData;
|
||||
}
|
||||
} catch (error) {
|
||||
// JSON解析失败时静默处理,不影响用户输入
|
||||
console.error('反序列化失败:', error);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
watch(() => serializationData.value, updateTableFromSerialization);
|
||||
|
||||
const openLink = async (rowIndex: number) => {
|
||||
const { platformLabel, isLinkSingle, linkID } = tableData.value[rowIndex];
|
||||
const link = openExternalLink(platformLabel, isLinkSingle, linkID);
|
||||
@@ -153,6 +251,36 @@ const openLink = async (rowIndex: number) => {
|
||||
const deleteRecord = (rowIndex: number) => {
|
||||
tableData.value.splice(rowIndex, 1);
|
||||
};
|
||||
|
||||
// 还原默认功能
|
||||
const resetToDefault = () => {
|
||||
const defaultData = [
|
||||
{
|
||||
factLabel: 95,
|
||||
showLabel: 100,
|
||||
platformLabel: '淘宝',
|
||||
isLinkSingle: true,
|
||||
value: 0.95,
|
||||
sort: 1,
|
||||
platformFee: 0,
|
||||
linkID: ''
|
||||
}
|
||||
];
|
||||
|
||||
tableData.value = defaultData;
|
||||
serializationData.value = '';
|
||||
Message.success('已还原为默认数据');
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
<style lang="less">
|
||||
.rate-table-container {
|
||||
.serialization-section {
|
||||
background: #f7f8fa;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e5e6eb;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user