pull/330/head
xiaojunnuo 2024-12-24 01:12:12 +08:00
parent cb27d4b490
commit ffa4de6911
14 changed files with 107 additions and 33 deletions

View File

@ -231,6 +231,7 @@ function openUpgrade() {
title: "基础版", title: "基础版",
desc: "社区免费版", desc: "社区免费版",
type: "free", type: "free",
icon: "lucide:package-open",
privilege: ["证书申请无限制", "域名数量无限制", "证书流水线数量无限制", "常用的主机、云平台、cdn等部署插件", "邮件、webhook通知方式"] privilege: ["证书申请无限制", "域名数量无限制", "证书流水线数量无限制", "常用的主机、云平台、cdn等部署插件", "邮件、webhook通知方式"]
}, },
plus: { plus: {
@ -244,6 +245,7 @@ function openUpgrade() {
openStarModal(); openStarModal();
} }
}, },
icon: "stash:thumb-up",
price: 29.9, price: 29.9,
get() { get() {
return ( return (
@ -259,6 +261,7 @@ function openUpgrade() {
title: "商业版", title: "商业版",
desc: "商业授权,可对外运营", desc: "商业授权,可对外运营",
type: "comm", type: "comm",
icon: "vaadin:handshake",
privilege: ["拥有专业版所有特权", "允许商用可修改logo、标题", "数据统计", "插件管理", "多用户无限制", "支持用户支付"], privilege: ["拥有专业版所有特权", "允许商用可修改logo、标题", "数据统计", "插件管理", "多用户无限制", "支持用户支付"],
price: 399, price: 399,
get() { get() {
@ -293,8 +296,8 @@ function openUpgrade() {
slots.push( slots.push(
<a-col span={8}> <a-col span={8}>
<div class={vipBlockClass}> <div class={vipBlockClass}>
<h3 class="block-header"> <h3 class="block-header ">
<span>{item.title}</span> <span class="flex-o">{item.title}</span>
{item.trial && ( {item.trial && (
<span class="trial"> <span class="trial">
<a-tooltip title={item.trial.message}> <a-tooltip title={item.trial.message}>
@ -303,8 +306,11 @@ function openUpgrade() {
</span> </span>
)} )}
</h3> </h3>
<div style="color:green">{item.desc}</div> <div style="color:green" class="flex-o">
<ul class="flex-1"> <fs-icon icon={item.icon} class="fs-16 flex-o" />
{item.desc}
</div>
<ul class="flex-1 privilege">
{item.privilege.map((p: string) => ( {item.privilege.map((p: string) => (
<li class="flex-baseline"> <li class="flex-baseline">
<fs-icon class="color-green" icon="ion:checkmark-sharp" /> <fs-icon class="color-green" icon="ion:checkmark-sharp" />
@ -430,9 +436,13 @@ onMounted(() => {
color: green; color: green;
} }
.vip-type-vs { .vip-type-vs {
.privilege {
.fs-icon {
color: green;
}
}
.fs-icon { .fs-icon {
margin-right: 5px; margin-right: 5px;
color: green;
} }
} }
} }

View File

@ -1,7 +1,7 @@
import Validator from "async-validator"; import Validator from "async-validator";
// 自定义验证器函数 // 自定义验证器函数
export function isDomain(rule: any, value: any) { export function isDomain(rule: any, value: any) {
if (value == null) { if (value == null || value == "") {
return true; return true;
} }
let domains: string[] = value; let domains: string[] = value;

View File

@ -216,7 +216,7 @@ export const sysResources = [
title: "用户套餐", title: "用户套餐",
name: "UserSuites", name: "UserSuites",
path: "/sys/suite/user-suite", path: "/sys/suite/user-suite",
component: "/certd/suite/user-suite/index.vue", component: "/sys/suite/user-suite/index.vue",
meta: { meta: {
show: () => { show: () => {
const settingStore = useSettingStore(); const settingStore = useSettingStore();

View File

@ -59,7 +59,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
order: 0, order: 0,
type: "link", type: "link",
text: null, text: null,
title: "立即检查", tooltip: {
title: "立即检查"
},
icon: "ion:play-sharp", icon: "ion:play-sharp",
click: async ({ row }) => { click: async ({ row }) => {
await api.DoCheck(row.id); await api.DoCheck(row.id);
@ -107,7 +109,10 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}, },
type: "text", type: "text",
form: { form: {
rules: [{ required: true, message: "请输入域名" }] rules: [
{ required: true, message: "请输入域名" },
{ type: "domains", message: "请输入正确的域名" }
]
}, },
column: { column: {
width: 160, width: 160,

View File

@ -76,7 +76,7 @@ async function orderCreate() {
payType: formRef.value.payType payType: formRef.value.payType
}); });
function onPaid() { async function onPaid() {
openRef.value = false; openRef.value = false;
router.push({ router.push({
path: "/" path: "/"
@ -114,7 +114,7 @@ async function orderCreate() {
} }
function doAlipay(paymentReq: any) { function doAlipay(paymentReq: any) {
window.open(paymentReq.api); window.open(paymentReq.url);
} }
async function doWxpay(qrcodeText: string, onPaid: () => Promise<void>) { async function doWxpay(qrcodeText: string, onPaid: () => Promise<void>) {
@ -157,7 +157,7 @@ function doYizhifu(paymentReq: any) {
* 签名类型 sign_type String MD5 默认为MD5 * 签名类型 sign_type String MD5 默认为MD5
*/ */
const form = document.createElement("form"); const form = document.createElement("form");
form.action = paymentReq.api; form.action = paymentReq.url;
form.method = "post"; form.method = "post";
form.target = "_blank"; form.target = "_blank";
// form.style.display = "none"; // form.style.display = "none";

View File

@ -11,7 +11,7 @@
<a-tab-pane key="register" tab="注册设置"> <a-tab-pane key="register" tab="注册设置">
<SettingRegister v-if="activeKey === 'register'" /> <SettingRegister v-if="activeKey === 'register'" />
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="payment" tab="支付设置"> <a-tab-pane v-if="settingsStore.isComm" key="payment" tab="支付设置">
<SettingPayment v-if="activeKey === 'payment'" /> <SettingPayment v-if="activeKey === 'payment'" />
</a-tab-pane> </a-tab-pane>
</a-tabs> </a-tabs>
@ -25,10 +25,11 @@ import SettingRegister from "/@/views/sys/settings/tabs/register.vue";
import SettingPayment from "/@/views/sys/settings/tabs/payment.vue"; import SettingPayment from "/@/views/sys/settings/tabs/payment.vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { ref } from "vue"; import { ref } from "vue";
import { useSettingStore } from "/@/store/modules/settings";
defineOptions({ defineOptions({
name: "SysSettings" name: "SysSettings"
}); });
const settingsStore = useSettingStore();
const activeKey = ref(""); const activeKey = ref("");
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();

View File

@ -68,7 +68,11 @@ const formState = reactive<
alipay: PaymentItem; alipay: PaymentItem;
wxpay: PaymentItem; wxpay: PaymentItem;
}> }>
>({}); >({
yizhifu: { enabled: false },
alipay: { enabled: false },
wxpay: { enabled: false }
});
async function loadSettings() { async function loadSettings() {
const data: any = await api.SettingGet(); const data: any = await api.SettingGet();

View File

@ -106,7 +106,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
] ]
}), }),
form: { form: {
rules: [{ required: true, message: "此项必填" }] value: "suite",
rules: [{ required: true, message: "此项必填" }],
helper: "目前没区别,重复购买可叠加数量"
}, },
column: { column: {
width: 80, width: 80,

View File

@ -3,7 +3,7 @@
<template #header> <template #header>
<div class="title"> <div class="title">
套餐设置 套餐设置
<span class="sub"> 需要<router-link to="/sys/settings" :query="{ tab: 'payment' }">开启至少一种支付方式</router-link></span> <span class="sub"> 需要<router-link :to="{ path: '/sys/settings', query: { tab: 'payment' } }">开启至少一种支付方式</router-link></span>
</div> </div>
</template> </template>
@ -31,6 +31,7 @@
<a-form-item label=" " :colon="false"> <a-form-item label=" " :colon="false">
<loading-button type="primary" html-type="button" :click="onClick">保存</loading-button> <loading-button type="primary" html-type="button" :click="onClick">保存</loading-button>
<div class="helper">需要 <router-link :to="{ path: '/sys/settings', query: { tab: 'payment' } }">开启至少一种支付方式</router-link></div>
</a-form-item> </a-form-item>
</a-form> </a-form>
</div> </div>

View File

@ -1,7 +1,7 @@
import { request } from "/src/api/service"; import { request } from "/src/api/service";
export function createApi() { export function createApi() {
const apiPrefix = "/sys/suite/userSuites"; const apiPrefix = "/sys/suite/user-suite";
return { return {
async GetList(query: any) { async GetList(query: any) {
return await request({ return await request({
@ -47,6 +47,14 @@ export function createApi() {
url: apiPrefix + "/all", url: apiPrefix + "/all",
method: "post" method: "post"
}); });
},
async GetSimpleUserByIds(ids: number[]) {
return await request({
url: "/sys/authority/user/getSimpleUserByIds",
method: "post",
data: { ids }
});
} }
}; };
} }

View File

@ -55,26 +55,26 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}, },
actionbar: { actionbar: {
buttons: { buttons: {
add: { show: false }, add: { show: false }
buy: { // buy: {
text: "购买", // text: "购买",
type: "primary", // type: "primary",
click() { // click() {
router.push({ // router.push({
path: "/certd/suite/buy" // path: "/certd/suite/buy"
}); // });
} // }
} // }
} }
}, },
rowHandle: { rowHandle: {
width: 200, width: 200,
fixed: "right", fixed: "right",
buttons: { buttons: {
view: { show: false }, view: { show: true },
copy: { show: false }, copy: { show: false },
edit: { show: false }, edit: { show: false },
remove: { show: false } remove: { show: true }
// continue:{ // continue:{
// text:"续期", // text:"续期",
// type:"link", // type:"link",
@ -115,6 +115,17 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
width: 200 width: 200
} }
}, },
userId: {
title: "用户",
type: "table-select",
dict: dict({
async getNodesByValues(ids: number[]) {
return await api.GetSimpleUserByIds(ids);
},
value: "id",
label: "nickName"
})
},
productType: { productType: {
title: "类型", title: "类型",
type: "dict-select", type: "dict-select",

View File

@ -39,14 +39,18 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
@Post('/add', { summary: Constants.per.authOnly }) @Post('/add', { summary: Constants.per.authOnly })
async add(@Body(ALL) bean: any) { async add(@Body(ALL) bean: any) {
bean.userId = this.getUserId(); bean.userId = this.getUserId();
return await super.add(bean); const res = await this.service.add(bean);
await this.service.check(res.id);
return this.ok(res);
} }
@Post('/update', { summary: Constants.per.authOnly }) @Post('/update', { summary: Constants.per.authOnly })
async update(@Body(ALL) bean) { async update(@Body(ALL) bean) {
await this.service.checkUserId(bean.id, this.getUserId()); await this.service.checkUserId(bean.id, this.getUserId());
delete bean.userId; delete bean.userId;
return await super.update(bean); await this.service.update(bean);
await this.service.check(bean.id);
return this.ok();
} }
@Post('/info', { summary: Constants.per.authOnly }) @Post('/info', { summary: Constants.per.authOnly })
async info(@Query('id') id: number) { async info(@Query('id') id: number) {

View File

@ -4,6 +4,7 @@ import { CrudController } from '@certd/lib-server';
import { RoleService } from '../../../modules/sys/authority/service/role-service.js'; import { RoleService } from '../../../modules/sys/authority/service/role-service.js';
import { PermissionService } from '../../../modules/sys/authority/service/permission-service.js'; import { PermissionService } from '../../../modules/sys/authority/service/permission-service.js';
import { Constants } from '@certd/lib-server'; import { Constants } from '@certd/lib-server';
import { In } from 'typeorm';
/** /**
* *
@ -23,6 +24,24 @@ export class UserController extends CrudController<UserService> {
return this.service; return this.service;
} }
@Post('/getSimpleUserByIds', { summary: 'sys:auth:user:add' })
async getSimpleUserByIds(@Body('ids') ids: number[]) {
const users = await this.service.find({
select: {
id: true,
username: true,
nickName: true,
mobile: true,
phoneCode: true,
},
where: {
id: In(ids),
},
});
return this.ok(users);
}
@Post('/page', { summary: 'sys:auth:user:view' }) @Post('/page', { summary: 'sys:auth:user:view' })
async page( async page(
@Body(ALL) @Body(ALL)

View File

@ -51,7 +51,16 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
} }
} }
return await this.repository.save(data); data.disabled = false;
return await super.add(data);
}
async update(data: any) {
if (!data.id) {
throw new Error('id is required');
}
delete data.userId;
await super.update(data);
} }
async getUserMonitorCount(userId: number) { async getUserMonitorCount(userId: number) {