This commit is contained in:
2026-05-31 12:09:14 +08:00
commit e4f9eedb80
1121 changed files with 209380 additions and 0 deletions
@@ -0,0 +1,154 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="商城名称" name="shopName">
<a-input
allow-clear
:maxlength="50"
placeholder="请输入商城名称"
v-model:value="form.shopName"
/>
</a-form-item>
<a-form-item label="商城Logo" name="shopLogo">
<SelectFile
:placeholder="'请选择商城Logo'"
:limit="1"
:data="logoList"
@done="chooseImage"
@del="onDeleteImage"
/>
</a-form-item>
<a-form-item label="商城描述" name="shopDesc">
<a-textarea
:rows="4"
:maxlength="500"
placeholder="请输入商城描述"
v-model:value="form.shopDesc"
/>
</a-form-item>
<a-form-item label="客服电话" name="shopPhone">
<a-input
allow-clear
:maxlength="20"
placeholder="请输入客服电话"
v-model:value="form.shopPhone"
/>
</a-form-item>
<a-form-item label="商城地址" name="shopAddress">
<a-input
allow-clear
:maxlength="200"
placeholder="请输入商城地址"
v-model:value="form.shopAddress"
/>
</a-form-item>
<a-form-item label="商城开关" name="shopEnabled">
<a-switch v-model:checked="form.shopEnabled" checked-children="开" un-checked-children="关" />
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, watch, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FileRecord } from '@/api/system/file/model';
const category = 'basic';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const logoList = ref<ItemType[]>([]);
const { form, assignFields } = useFormData<ShopSetting>({
shopName: '',
shopLogo: '',
shopDesc: '',
shopPhone: '',
shopAddress: '',
shopEnabled: true
});
const rules = reactive({
shopName: [
{
required: true,
message: '请输入商城名称',
type: 'string',
trigger: 'blur'
}
]
});
// 选择图片
const chooseImage = (data: FileRecord) => {
logoList.value.push({ uid: data.id, url: data.path, status: 'done' });
form.shopLogo = data.path;
};
// 删除图片
const onDeleteImage = (index: number) => {
logoList.value.splice(index, 1);
form.shopLogo = '';
};
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 布尔值转换(后端可能返回字符串 '1'/'0'/'true'/'false')
const raw = (values as any).shopEnabled;
form.shopEnabled = raw === true || raw === 'true' || raw === '1' || raw === 1;
// 回显 Logo
logoList.value = [];
if (form.shopLogo) {
logoList.value.push({ uid: 1, url: form.shopLogo as string, status: '' });
}
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,164 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="启用分销功能" name="dealerEnabled">
<a-switch
v-model:checked="form.dealerEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="开放申请分销商" name="dealerApplyEnabled">
<a-switch
v-model:checked="form.dealerApplyEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="一级佣金比例" name="dealerLevel1Rate">
<a-input-number
v-model:value="form.dealerLevel1Rate"
:min="0"
:max="100"
:precision="2"
placeholder="请输入比例"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">%</span>
</a-form-item>
<a-form-item label="二级佣金比例" name="dealerLevel2Rate">
<a-input-number
v-model:value="form.dealerLevel2Rate"
:min="0"
:max="100"
:precision="2"
placeholder="请输入比例"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">%</span>
</a-form-item>
<a-form-item label="最低提现金额" name="dealerWithdrawMin">
<a-input-number
v-model:value="form.dealerWithdrawMin"
:min="0"
:precision="2"
placeholder="请输入最低提现金额"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">元</span>
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'dealer';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
dealerEnabled: false,
dealerApplyEnabled: false,
dealerLevel1Rate: 10,
dealerLevel2Rate: 5,
dealerWithdrawMin: 10
});
const rules = reactive({
dealerLevel1Rate: [
{
required: true,
message: '请输入一级佣金比例',
type: 'number',
trigger: 'blur'
}
],
dealerLevel2Rate: [
{
required: true,
message: '请输入二级佣金比例',
type: 'number',
trigger: 'blur'
}
],
dealerWithdrawMin: [
{
required: true,
message: '请输入最低提现金额',
type: 'number',
trigger: 'blur'
}
]
});
const toBool = (val: any): boolean =>
val === true || val === 'true' || val === '1' || val === 1;
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 布尔值转换
form.dealerEnabled = toBool((values as any).dealerEnabled);
form.dealerApplyEnabled = toBool((values as any).dealerApplyEnabled);
// 数值字段转换
if (values.dealerLevel1Rate !== undefined) {
form.dealerLevel1Rate = Number(values.dealerLevel1Rate);
}
if (values.dealerLevel2Rate !== undefined) {
form.dealerLevel2Rate = Number(values.dealerLevel2Rate);
}
if (values.dealerWithdrawMin !== undefined) {
form.dealerWithdrawMin = Number(values.dealerWithdrawMin);
}
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,206 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-divider orientation="left">小程序基础信息</a-divider>
<a-form-item label="AppID" name="miniAppId">
<a-input
allow-clear
:maxlength="50"
placeholder="请输入微信小程序 AppID"
v-model:value="form.miniAppId"
/>
<div class="ant-form-item-explain">微信公众平台 → 开发管理 → 开发设置 → AppID</div>
</a-form-item>
<a-form-item label="AppSecret" name="miniAppSecret">
<a-input-password
allow-clear
:maxlength="100"
placeholder="请输入微信小程序 AppSecret"
v-model:value="form.miniAppSecret"
/>
<div class="ant-form-item-explain">用于服务端接口调用(登录、支付等),请妥善保管</div>
</a-form-item>
<a-divider orientation="left">小程序页面配置</a-divider>
<a-form-item label="首页路径" name="miniHomePath">
<a-input
allow-clear
:maxlength="200"
placeholder="如 pages/index/index"
v-model:value="form.miniHomePath"
/>
</a-form-item>
<a-form-item label="分享标题" name="miniShareTitle">
<a-input
allow-clear
:maxlength="50"
placeholder="默认分享到好友/朋友圈的标题"
v-model:value="form.miniShareTitle"
/>
</a-form-item>
<a-form-item label="分享图片" name="miniShareImage">
<SelectFile
:placeholder="'请选择分享图片'"
:limit="1"
:data="shareImageList"
@done="chooseShareImage"
@del="onDeleteShareImage"
/>
<div class="ant-form-item-explain">建议尺寸:500x400 像素,不超过 1MB</div>
</a-form-item>
<a-divider orientation="left">客服与联系</a-divider>
<a-form-item label="客服开关" name="miniKfEnabled">
<a-switch
v-model:checked="form.miniKfEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="客服类型" name="miniKfType">
<a-radio-group v-model:value="form.miniKfType">
<a-radio value="button">悬浮按钮</a-radio>
<a-radio value="menu">菜单入口</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="联系客服" name="miniContactPhone">
<a-input
allow-clear
:maxlength="30"
placeholder="联系电话或客服微信号"
v-model:value="form.miniContactPhone"
/>
</a-form-item>
<a-divider orientation="left">订阅消息</a-divider>
<a-form-item label="订阅开关" name="miniSubscribeEnabled">
<a-switch
v-model:checked="form.miniSubscribeEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="模板ID" name="miniSubscribeTemplateIds">
<a-textarea
:rows="3"
:maxlength="500"
placeholder="多个模板ID用英文逗号分隔"
v-model:value="form.miniSubscribeTemplateIds"
/>
<div class="ant-form-item-explain">微信公众平台 → 功能 → 订阅消息 → 公共模板库</div>
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FileRecord } from '@/api/system/file/model';
const category = 'miniprogram';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const shareImageList = ref<ItemType[]>([]);
const { form, assignFields } = useFormData<ShopSetting>({
miniAppId: '',
miniAppSecret: '',
miniHomePath: '',
miniShareTitle: '',
miniShareImage: '',
miniKfEnabled: false,
miniKfType: 'button',
miniContactPhone: '',
miniSubscribeEnabled: false,
miniSubscribeTemplateIds: ''
});
const rules = reactive({
miniAppId: [
{
required: true,
message: '请输入微信小程序 AppID',
type: 'string',
trigger: 'blur'
}
]
});
const toBool = (val: any): boolean =>
val === true || val === 'true' || val === '1' || val === 1;
// 选择分享图片
const chooseShareImage = (data: FileRecord) => {
shareImageList.value.push({ uid: data.id, url: data.path, status: 'done' });
form.miniShareImage = data.path;
};
// 删除分享图片
const onDeleteShareImage = (index: number) => {
shareImageList.value.splice(index, 1);
form.miniShareImage = '';
};
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 布尔值转换
form.miniKfEnabled = toBool((values as any).miniKfEnabled);
form.miniSubscribeEnabled = toBool((values as any).miniSubscribeEnabled);
// 回显分享图片
shareImageList.value = [];
if (form.miniShareImage) {
shareImageList.value.push({ uid: 1, url: form.miniShareImage as string, status: '' });
}
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,87 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '120px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="订单通知" name="notifyOrderEnabled">
<a-switch v-model:checked="form.notifyOrderEnabled" />
<span class="ele-text-secondary" style="margin-left: 8px; font-size: 12px;">用户下单后发送通知</span>
</a-form-item>
<a-form-item label="支付通知" name="notifyPayEnabled">
<a-switch v-model:checked="form.notifyPayEnabled" />
<span class="ele-text-secondary" style="margin-left: 8px; font-size: 12px;">用户支付成功后发送通知</span>
</a-form-item>
<a-form-item label="退款通知" name="notifyRefundEnabled">
<a-switch v-model:checked="form.notifyRefundEnabled" />
<span class="ele-text-secondary" style="margin-left: 8px; font-size: 12px;">退款申请/完成后发送通知</span>
</a-form-item>
<a-form-item label="发货通知" name="notifyShipEnabled">
<a-switch v-model:checked="form.notifyShipEnabled" />
<span class="ele-text-secondary" style="margin-left: 8px; font-size: 12px;">商家发货后发送通知</span>
</a-form-item>
<a-form-item label="签收通知" name="notifySignEnabled">
<a-switch v-model:checked="form.notifySignEnabled" />
<span class="ele-text-secondary" style="margin-left: 8px; font-size: 12px;">用户确认收货后发送通知</span>
</a-form-item>
<a-form-item :wrapper-col="styleResponsive ? { md: { offset: 3, span: 9 } } : { flex: '1' }">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'notify';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
notifyOrderEnabled: true,
notifyPayEnabled: true,
notifyRefundEnabled: true,
notifyShipEnabled: true,
notifySignEnabled: true,
});
const toBool = (v: any): boolean => v === true || v === 1 || v === '1' || v === 'true';
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
form.notifyOrderEnabled = toBool((values as any).notifyOrderEnabled);
form.notifyPayEnabled = toBool((values as any).notifyPayEnabled);
form.notifyRefundEnabled = toBool((values as any).notifyRefundEnabled);
form.notifyShipEnabled = toBool((values as any).notifyShipEnabled);
form.notifySignEnabled = toBool((values as any).notifySignEnabled);
}
} catch (e) {
// 未配置时忽略
}
};
const save = () => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => { message.success('保存成功'); })
.catch((e: any) => { message.error(e.message); })
.finally(() => { loading.value = false; });
};
onMounted(load);
</script>
@@ -0,0 +1,165 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="自动确认收货天数" name="orderAutoConfirmDays">
<a-input-number
v-model:value="form.orderAutoConfirmDays"
:min="1"
:max="365"
placeholder="请输入天数"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">天</span>
</a-form-item>
<a-form-item label="未付款自动关闭" name="orderAutoCloseMins">
<a-input-number
v-model:value="form.orderAutoCloseMins"
:min="1"
:max="10080"
placeholder="请输入分钟数"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">分钟</span>
</a-form-item>
<a-form-item label="自动好评天数" name="orderAutoCommentDays">
<a-input-number
v-model:value="form.orderAutoCommentDays"
:min="1"
:max="365"
placeholder="请输入天数"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">天</span>
</a-form-item>
<a-form-item label="免运费金额" name="orderFreightFreeAmount">
<a-input-number
v-model:value="form.orderFreightFreeAmount"
:min="0"
:precision="2"
placeholder="0 表示不免运费"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">元(0 表示不免运费)</span>
</a-form-item>
<a-form-item label="允许退款" name="orderRefundEnabled">
<a-switch
v-model:checked="form.orderRefundEnabled"
checked-children="是"
un-checked-children="否"
/>
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'order';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
orderAutoConfirmDays: 7,
orderAutoCloseMins: 30,
orderAutoCommentDays: 15,
orderFreightFreeAmount: 0,
orderRefundEnabled: true
});
const rules = reactive({
orderAutoConfirmDays: [
{
required: true,
message: '请输入自动确认收货天数',
type: 'number',
trigger: 'blur'
}
],
orderAutoCloseMins: [
{
required: true,
message: '请输入未付款自动关闭分钟数',
type: 'number',
trigger: 'blur'
}
],
orderAutoCommentDays: [
{
required: true,
message: '请输入自动好评天数',
type: 'number',
trigger: 'blur'
}
]
});
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 布尔值转换
const raw = (values as any).orderRefundEnabled;
form.orderRefundEnabled = raw === true || raw === 'true' || raw === '1' || raw === 1;
// 数值字段转换(后端返回字符串时)
if (values.orderAutoConfirmDays !== undefined) {
form.orderAutoConfirmDays = Number(values.orderAutoConfirmDays);
}
if (values.orderAutoCloseMins !== undefined) {
form.orderAutoCloseMins = Number(values.orderAutoCloseMins);
}
if (values.orderAutoCommentDays !== undefined) {
form.orderAutoCommentDays = Number(values.orderAutoCommentDays);
}
if (values.orderFreightFreeAmount !== undefined) {
form.orderFreightFreeAmount = Number(values.orderFreightFreeAmount);
}
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,158 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="余额支付" name="payBalanceEnabled">
<a-switch
v-model:checked="form.payBalanceEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="微信支付" name="payWechatEnabled">
<a-switch
v-model:checked="form.payWechatEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="微信AppID" name="payWechatAppId">
<a-input
allow-clear
:maxlength="50"
placeholder="请输入微信AppID"
v-model:value="form.payWechatAppId"
/>
</a-form-item>
<a-form-item label="微信商户号" name="payWechatMchId">
<a-input
allow-clear
:maxlength="50"
placeholder="请输入微信商户号"
v-model:value="form.payWechatMchId"
/>
</a-form-item>
<a-form-item label="微信支付APIv3密钥" name="payWechatApiKey">
<a-input-password
allow-clear
:maxlength="200"
placeholder="请输入微信支付APIv3密钥"
v-model:value="form.payWechatApiKey"
/>
</a-form-item>
<div style="margin-bottom: 22px; width: 750px">
<a-divider>支付宝支付</a-divider>
</div>
<a-form-item label="支付宝支付" name="payAlipayEnabled">
<a-switch
v-model:checked="form.payAlipayEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="支付宝AppID" name="payAlipayAppId">
<a-input
allow-clear
:maxlength="50"
placeholder="请输入支付宝AppID"
v-model:value="form.payAlipayAppId"
/>
</a-form-item>
<a-form-item label="商户私钥" name="payAlipayPrivateKey">
<a-textarea
:rows="4"
placeholder="请输入商户私钥"
v-model:value="form.payAlipayPrivateKey"
/>
</a-form-item>
<a-form-item label="支付宝公钥" name="payAlipayPublicKey">
<a-textarea
:rows="4"
placeholder="请输入支付宝公钥"
v-model:value="form.payAlipayPublicKey"
/>
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'payment';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
payBalanceEnabled: false,
payWechatEnabled: false,
payWechatAppId: '',
payWechatMchId: '',
payWechatApiKey: '',
payAlipayEnabled: false,
payAlipayAppId: '',
payAlipayPrivateKey: '',
payAlipayPublicKey: ''
});
const rules = reactive({});
const toBool = (val: any): boolean =>
val === true || val === 'true' || val === '1' || val === 1;
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 布尔值转换
form.payBalanceEnabled = toBool((values as any).payBalanceEnabled);
form.payWechatEnabled = toBool((values as any).payWechatEnabled);
form.payAlipayEnabled = toBool((values as any).payAlipayEnabled);
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,162 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="启用积分功能" name="pointsEnabled">
<a-switch
v-model:checked="form.pointsEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="启用签到积分" name="pointsSigninEnabled">
<a-switch
v-model:checked="form.pointsSigninEnabled"
checked-children="开"
un-checked-children="关"
/>
</a-form-item>
<a-form-item label="每日签到积分" name="pointsSigninAmount">
<a-input-number
v-model:value="form.pointsSigninAmount"
:min="1"
:max="99999"
placeholder="请输入签到积分数"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">积分</span>
</a-form-item>
<a-form-item label="消费积分比例" name="pointsOrderRate">
<a-input-number
v-model:value="form.pointsOrderRate"
:min="1"
:max="99999"
placeholder="请输入比例"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">每消费 N 元得 1 积分</span>
</a-form-item>
<a-form-item label="积分兑换比例" name="pointsExchangeRate">
<a-input-number
v-model:value="form.pointsExchangeRate"
:min="1"
:max="99999"
placeholder="请输入比例"
style="width: 200px"
/>
<span style="margin-left: 8px; color: #888">N 积分兑换 1 元</span>
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'points';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
pointsEnabled: true,
pointsSigninEnabled: true,
pointsSigninAmount: 10,
pointsOrderRate: 10,
pointsExchangeRate: 100
});
const rules = reactive({
pointsSigninAmount: [
{
required: true,
message: '请输入每日签到积分数',
type: 'number',
trigger: 'blur'
}
],
pointsOrderRate: [
{
required: true,
message: '请输入消费积分比例',
type: 'number',
trigger: 'blur'
}
],
pointsExchangeRate: [
{
required: true,
message: '请输入积分兑换比例',
type: 'number',
trigger: 'blur'
}
]
});
const toBool = (val: any): boolean =>
val === true || val === 'true' || val === '1' || val === 1;
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 布尔值转换
form.pointsEnabled = toBool((values as any).pointsEnabled);
form.pointsSigninEnabled = toBool((values as any).pointsSigninEnabled);
// 数值字段转换
if (values.pointsSigninAmount !== undefined) {
form.pointsSigninAmount = Number(values.pointsSigninAmount);
}
if (values.pointsOrderRate !== undefined) {
form.pointsOrderRate = Number(values.pointsOrderRate);
}
if (values.pointsExchangeRate !== undefined) {
form.pointsExchangeRate = Number(values.pointsExchangeRate);
}
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,192 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="短信平台" name="smsType">
<a-radio-group v-model:value="form.smsType">
<a-radio :value="1">阿里云</a-radio>
<a-radio :value="2">腾讯云</a-radio>
<a-radio :value="3">七牛云</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="AccessKeyId" name="accessKeyId">
<a-input
v-model:value="form.accessKeyId"
placeholder="请输入 AccessKeyId"
allow-clear
:maxlength="100"
/>
</a-form-item>
<a-form-item label="AccessKeySecret" name="accessKeySecret">
<a-input-password
v-model:value="form.accessKeySecret"
placeholder="请输入 AccessKeySecret"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="短信签名" name="smsSign">
<a-input
v-model:value="form.smsSign"
placeholder="请输入短信签名"
allow-clear
:maxlength="20"
/>
</a-form-item>
<a-divider orientation="left">短信验证码(通知用户)</a-divider>
<a-form-item label="是否开启" name="smsNoticeUser">
<a-radio-group v-model:value="form.smsNoticeUser">
<a-radio :value="1">开启</a-radio>
<a-radio :value="0">关闭</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="模板内容">
<span class="ele-text-secondary">
验证码 ${code},您正在进行身份验证,请勿泄露给他人!
</span>
</a-form-item>
<a-form-item label="模板ID" name="smsUserTemplateId">
<a-input
v-model:value="form.smsUserTemplateId"
placeholder="请输入用户验证码模板ID"
allow-clear
:maxlength="100"
/>
</a-form-item>
<a-divider orientation="left">新订单通知(通知商家)</a-divider>
<a-form-item label="是否开启" name="smsNoticeMerchant">
<a-radio-group v-model:value="form.smsNoticeMerchant">
<a-radio :value="1">开启</a-radio>
<a-radio :value="0">关闭</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="模板ID" name="smsMerchantTemplateId">
<a-input
v-model:value="form.smsMerchantTemplateId"
placeholder="请输入商家通知模板ID"
allow-clear
:maxlength="100"
/>
</a-form-item>
<a-form-item label="接收手机号" name="smsMerchantMobiles">
<a-input
v-model:value="form.smsMerchantMobiles"
placeholder="多个手机号用英文逗号分隔"
allow-clear
:maxlength="200"
/>
<div class="ele-text-secondary" style="font-size: 12px">
新订单将向以上手机号发送短信通知
</div>
</a-form-item>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'sms';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
smsType: 1,
accessKeyId: '',
accessKeySecret: '',
smsSign: '',
smsNoticeUser: 1,
smsUserTemplateId: '',
smsNoticeMerchant: 1,
smsMerchantTemplateId: '',
smsMerchantMobiles: ''
});
const rules = reactive({
accessKeyId: [
{
required: true,
message: '请输入 AccessKeyId',
type: 'string',
trigger: 'blur'
}
],
accessKeySecret: [
{
required: true,
message: '请输入 AccessKeySecret',
type: 'string',
trigger: 'blur'
}
],
smsSign: [
{
required: true,
message: '请输入短信签名',
type: 'string',
trigger: 'blur'
}
]
});
const toBool = (val: any): number =>
val === true || val === 'true' || val === '1' || val === 1 ? 1 : 0;
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
// 数字/布尔值转换
form.smsNoticeUser = toBool((values as any).smsNoticeUser);
form.smsNoticeMerchant = toBool((values as any).smsNoticeMerchant);
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
@@ -0,0 +1,215 @@
<template>
<a-card :bordered="false">
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="styleResponsive ? { md: 3, sm: 5, xs: 24 } : { flex: '90px' }"
:wrapper-col="styleResponsive ? { md: 9, sm: 19, xs: 24 } : { flex: '1' }"
>
<a-form-item label="默认上传方式" name="uploadMethod">
<a-radio-group v-model:value="form.uploadMethod">
<a-radio-button value="file">本地</a-radio-button>
<a-radio-button value="oss">阿里云 OSS</a-radio-button>
<a-radio-button value="cos">腾讯云 COS</a-radio-button>
<a-radio-button value="kodo">七牛云 Kodo</a-radio-button>
</a-radio-group>
</a-form-item>
<template v-if="form.uploadMethod === 'file'">
<a-form-item label="文件域名" name="fileUrl">
<a-input
v-model:value="form.fileUrl"
placeholder="请输入文件服务器域名,如 https://file.example.com"
allow-clear
:maxlength="200"
/>
</a-form-item>
</template>
<template v-if="form.uploadMethod === 'oss'">
<a-form-item label="存储空间名称" name="bucketName">
<a-input v-model:value="form.bucketName" placeholder="Bucket Name" allow-clear :maxlength="100" />
</a-form-item>
<a-form-item label="Region 域名" name="bucketEndpoint">
<a-input
v-model:value="form.bucketEndpoint"
placeholder="如 https://oss-cn-shenzhen.aliyuncs.com"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="AccessKeyId" name="accessKeyId">
<a-input v-model:value="form.accessKeyId" placeholder="AccessKeyId" allow-clear :maxlength="100" />
</a-form-item>
<a-form-item label="AccessKeySecret" name="accessKeySecret">
<a-input-password
v-model:value="form.accessKeySecret"
placeholder="AccessKeySecret"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="空间域名" name="bucketDomain">
<a-input
v-model:value="form.bucketDomain"
placeholder="如 https://your-bucket.oss-cn-shenzhen.aliyuncs.com"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="申请地址">
<a href="https://oss.console.aliyun.com" target="_blank">https://oss.console.aliyun.com</a>
</a-form-item>
</template>
<template v-if="form.uploadMethod === 'cos'">
<a-form-item label="存储空间名称" name="bucketName">
<a-input v-model:value="form.bucketName" placeholder="Bucket Name" allow-clear :maxlength="100" />
</a-form-item>
<a-form-item label="Region" name="bucketEndpoint">
<a-input
v-model:value="form.bucketEndpoint"
placeholder="如 ap-guangzhou"
allow-clear
:maxlength="100"
/>
</a-form-item>
<a-form-item label="SecretId" name="accessKeyId">
<a-input v-model:value="form.accessKeyId" placeholder="SecretId" allow-clear :maxlength="100" />
</a-form-item>
<a-form-item label="SecretKey" name="accessKeySecret">
<a-input-password
v-model:value="form.accessKeySecret"
placeholder="SecretKey"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="空间域名" name="bucketDomain">
<a-input
v-model:value="form.bucketDomain"
placeholder="如 https://your-bucket.cos.ap-guangzhou.myqcloud.com"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="申请地址">
<a href="https://console.cloud.tencent.com/cos" target="_blank">
https://console.cloud.tencent.com/cos
</a>
</a-form-item>
</template>
<template v-if="form.uploadMethod === 'kodo'">
<a-form-item label="存储空间名称" name="bucketName">
<a-input v-model:value="form.bucketName" placeholder="Bucket Name" allow-clear :maxlength="100" />
</a-form-item>
<a-form-item label="AccessKey" name="accessKeyId">
<a-input v-model:value="form.accessKeyId" placeholder="AccessKey" allow-clear :maxlength="100" />
</a-form-item>
<a-form-item label="SecretKey" name="accessKeySecret">
<a-input-password
v-model:value="form.accessKeySecret"
placeholder="SecretKey"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="空间域名" name="bucketDomain">
<a-input
v-model:value="form.bucketDomain"
placeholder="如 https://your-bucket.qiniudn.com"
allow-clear
:maxlength="200"
/>
</a-form-item>
<a-form-item label="申请地址">
<a href="https://www.qiniu.com/products/kodo" target="_blank">
https://www.qiniu.com/products/kodo
</a>
</a-form-item>
</template>
<a-form-item label="保存">
<a-button type="primary" :loading="loading" @click="save">保存</a-button>
</a-form-item>
</a-form>
</a-card>
</template>
<script lang="ts" setup>
import { reactive, ref, onMounted } from 'vue';
import { message } from 'ant-design-vue';
import { useThemeStore } from '@/store/modules/theme';
import { storeToRefs } from 'pinia';
import { FormInstance } from 'ant-design-vue/es/form';
import useFormData from '@/utils/use-form-data';
import { batchSaveShopSetting, getShopSettingCategoryValues } from '@/api/shop/shopSetting';
import type { ShopSetting } from '@/api/shop/shopSetting/model';
const category = 'upload';
const themeStore = useThemeStore();
const { styleResponsive } = storeToRefs(themeStore);
const loading = ref(false);
const formRef = ref<FormInstance | null>(null);
const { form, assignFields } = useFormData<ShopSetting>({
uploadMethod: 'oss',
fileUrl: '',
bucketName: '',
bucketEndpoint: '',
accessKeyId: '',
accessKeySecret: '',
bucketDomain: ''
});
const rules = reactive({
uploadMethod: [
{
required: true,
message: '请选择上传方式',
type: 'string',
trigger: 'change'
}
]
});
const toBool = (val: any): boolean =>
val === true || val === 'true' || val === '1' || val === 1;
const load = async () => {
try {
const values = await getShopSettingCategoryValues(category);
if (values && Object.keys(values).length > 0) {
assignFields(values);
}
} catch (e: any) {
// 未配置时忽略
}
};
const save = () => {
if (!formRef.value) return;
formRef.value
.validate()
.then(() => {
loading.value = true;
batchSaveShopSetting(category, form as Record<string, unknown>)
.then(() => {
message.success('保存成功');
})
.catch((e) => {
message.error(e.message);
})
.finally(() => {
loading.value = false;
});
})
.catch(() => {});
};
onMounted(load);
</script>
+59
View File
@@ -0,0 +1,59 @@
<template>
<div class="ele-body ele-body-card">
<a-card
:bordered="false"
:body-style="{ paddingTop: '0px', minHeight: '800px' }"
>
<a-tabs v-model:active-key="active">
<a-tab-pane tab="基础设置" key="basic">
<Basic />
</a-tab-pane>
<a-tab-pane tab="订单设置" key="order">
<Order />
</a-tab-pane>
<a-tab-pane tab="积分设置" key="points">
<Points />
</a-tab-pane>
<a-tab-pane tab="分销设置" key="dealer">
<Dealer />
</a-tab-pane>
<a-tab-pane tab="支付设置" key="payment">
<Payment />
</a-tab-pane>
<a-tab-pane tab="通知设置" key="notify">
<Notify />
</a-tab-pane>
<a-tab-pane tab="上传设置" key="upload">
<Upload />
</a-tab-pane>
<a-tab-pane tab="短信设置" key="sms">
<Sms />
</a-tab-pane>
<a-tab-pane tab="小程序设置" key="miniprogram">
<Miniprogram />
</a-tab-pane>
</a-tabs>
</a-card>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import Basic from './components/basic.vue';
import Order from './components/order.vue';
import Points from './components/points.vue';
import Dealer from './components/dealer.vue';
import Payment from './components/payment.vue';
import Notify from './components/notify.vue';
import Upload from './components/upload.vue';
import Sms from './components/sms.vue';
import Miniprogram from './components/miniprogram.vue';
const active = ref('basic');
</script>
<script lang="ts">
export default {
name: 'ShopSetting'
};
</script>