+
{{ item.FieldName }}:
- {{ formatFieldValue(item.FieldValue) }}
+
+ {{ truncateValue(item.FieldValue) }}
+
+ {{ formatFieldValue(item.FieldValue) }}
-
@@ -114,6 +117,21 @@ const formatFieldValue = (value: any) => {
return String(value);
};
+// 判断是否为长文本
+const isLongValue = (value: any) => {
+ const str = formatFieldValue(value);
+ return str.length > 50;
+};
+
+// 截断长文本
+const truncateValue = (value: any) => {
+ const str = formatFieldValue(value);
+ if (str.length > 50) {
+ return str.substring(0, 50) + '...';
+ }
+ return str;
+};
+
// 分页
const onSizeChange = () => {
queryParams.pageNum = 1;
@@ -130,8 +148,13 @@ defineExpose({
From aec7f3a0174b1ee151f4246ade4093b3545ded70 Mon Sep 17 00:00:00 2001
From: WUSIJIAN <13825895+wsj0228@user.noreply.gitee.com>
Date: Tue, 20 Jan 2026 17:54:29 +0800
Subject: [PATCH 26/31] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E8=B5=84=E4=BA=A7?=
=?UTF-8?q?=E6=9C=8D=E5=8A=A1=E8=AE=A2=E9=98=85=E5=8A=9F=E8=83=BD,?=
=?UTF-8?q?=E5=9C=A8=E5=BA=94=E7=94=A8=E4=B8=AD=E9=9B=86=E6=88=90=E6=A8=A1?=
=?UTF-8?q?=E5=9D=97=E6=9C=AA=E5=BC=80=E9=80=9A=E5=BC=B9=E7=AA=97=E7=BB=84?=
=?UTF-8?q?=E4=BB=B6,=E5=BD=93=E6=A3=80=E6=B5=8B=E5=88=B0402=E7=8A=B6?=
=?UTF-8?q?=E6=80=81=E7=A0=81=E6=97=B6=E8=87=AA=E5=8A=A8=E5=BC=B9=E5=87=BA?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E5=AF=B9=E8=AF=9D=E6=A1=86=E5=BC=95=E5=AF=BC?=
=?UTF-8?q?=E7=94=A8=E6=88=B7=E5=BC=80=E9=80=9A=E6=9C=8D=E5=8A=A1,?=
=?UTF-8?q?=E5=90=8C=E6=97=B6=E6=96=B0=E5=A2=9E=E8=B5=84=E4=BA=A7=E8=AE=A2?=
=?UTF-8?q?=E9=98=85=E7=9B=B8=E5=85=B3API=E6=8E=A5=E5=8F=A3=E5=8C=85?=
=?UTF-8?q?=E6=8B=AC=E8=8E=B7=E5=8F=96=E8=B5=84=E4=BA=A7SKU=E4=BF=A1?=
=?UTF-8?q?=E6=81=AF=E5=92=8C=E8=AE=A2=E9=98=85=E6=9C=8D=E5=8A=A1=E6=8E=A5?=
=?UTF-8?q?=E5=8F=A3,=E5=9C=A8=E8=AF=B7=E6=B1=82=E6=8B=A6=E6=88=AA?=
=?UTF-8?q?=E5=99=A8=E4=B8=AD=E6=B7=BB=E5=8A=A0402=E7=8A=B6=E6=80=81?=
=?UTF-8?q?=E7=A0=81=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=E5=B9=B6=E8=BF=87?=
=?UTF-8?q?=E6=BB=A4SKU=E6=9F=A5=E8=AF=A2=E6=8E=A5=E5=8F=A3=E9=81=BF?=
=?UTF-8?q?=E5=85=8D=E5=BE=AA=E7=8E=AF=E8=A7=A6=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/App.vue | 11 +-
src/api/assets/asset/index.ts | 24 ++
src/components/assetSubscribe/index.vue | 277 ++++++++++++++++++++++++
src/utils/assetSubscribe.ts | 83 +++++++
src/utils/request.ts | 28 ++-
5 files changed, 420 insertions(+), 3 deletions(-)
create mode 100644 src/components/assetSubscribe/index.vue
create mode 100644 src/utils/assetSubscribe.ts
diff --git a/src/App.vue b/src/App.vue
index c06763c..61073d9 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,6 +4,12 @@
+
+
@@ -19,10 +25,12 @@ import setIntroduction from '/@/utils/setIconfont';
import LockScreen from '/@/layout/lockScreen/index.vue';
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
+import AssetSubscribeDialog from '/@/components/assetSubscribe/index.vue';
+import { assetSubscribeState } from '/@/utils/assetSubscribe';
export default defineComponent({
name: 'app',
- components: { LockScreen, Setings, CloseFull },
+ components: { LockScreen, Setings, CloseFull, AssetSubscribeDialog },
setup() {
const { proxy } =
getCurrentInstance();
const setingsRef = ref();
@@ -89,6 +97,7 @@ export default defineComponent({
themeConfig,
setingsRef,
getGlobalComponentSize,
+ assetSubscribeState,
...toRefs(state),
};
},
diff --git a/src/api/assets/asset/index.ts b/src/api/assets/asset/index.ts
index 8fc2218..f36e3f9 100644
--- a/src/api/assets/asset/index.ts
+++ b/src/api/assets/asset/index.ts
@@ -119,6 +119,15 @@ export function listAssetSkus(params: SkuQueryParams) {
});
}
+// 根据assetId获取资产和SKU信息(用于套餐开通弹窗)
+export function getAssetAndSku(params: { assetId: string }) {
+ return newService({
+ url: '/assets/asset/getAssetAndSku',
+ method: 'get',
+ params,
+ });
+}
+
// 创建 SKU
export function createAssetSku(data: CreateSkuParams) {
return newService({
@@ -220,3 +229,18 @@ export function listLogs(params: LogQueryParams) {
params,
});
}
+
+// 订阅/开通资产服务参数
+export interface SubscribeAssetParams {
+ skuId: string;
+ assetId?: string;
+}
+
+// 订阅/开通资产服务
+export function subscribeAsset(data: SubscribeAssetParams) {
+ return newService({
+ url: '/assets/asset/subscribe',
+ method: 'post',
+ data,
+ });
+}
diff --git a/src/components/assetSubscribe/index.vue b/src/components/assetSubscribe/index.vue
new file mode 100644
index 0000000..c4ce99d
--- /dev/null
+++ b/src/components/assetSubscribe/index.vue
@@ -0,0 +1,277 @@
+
+
+
+
+
+ {{ serviceName }}
+ 服务未开通,请选择套餐进行开通
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ sku.specsCount }}
+ {{ sku.specsUnit?.value || '' }}
+
+
+ ¥
+ {{ (sku.price / 100).toFixed(2) }}
+
+
+
+
+
+
+
+ 取消
+
+ 立即开通
+
+
+
+
+
+
+
+
diff --git a/src/utils/assetSubscribe.ts b/src/utils/assetSubscribe.ts
new file mode 100644
index 0000000..1df0289
--- /dev/null
+++ b/src/utils/assetSubscribe.ts
@@ -0,0 +1,83 @@
+import { ref } from 'vue';
+
+// 路由路径与 assetId 的映射关系
+const ROUTE_ASSET_MAP: Record = {
+ // CID广告业务(聚合广告)
+ '/cidService': { assetId: '696f423705e496ba4ccbe665', serviceName: '聚合广告' },
+
+ // AI客服业务
+ '/customerService': { assetId: '696f421205e496ba4ccbe662', serviceName: 'AI客服' },
+
+ // 聚合电商业务(资产管理)
+ '/assets': { assetId: '696b4acd1be1c8b76c4b4c15', serviceName: '资产管理' },
+
+ // 订单
+ // '/order': { assetId: '696b4acd1be1c8b76c4b4c15', serviceName: '资产管理' },
+
+ // AI数字人
+ // '/digitalHuman': { assetId: '696f421205e496ba4ccbe662', serviceName: 'AI客服' },
+};
+
+// 当前弹窗状态(响应式,供组件使用)
+export const assetSubscribeState = ref({
+ visible: false,
+ assetId: '',
+ serviceName: '',
+});
+
+/**
+ * 根据路由路径获取对应的 assetId 和服务名称
+ */
+export function getAssetInfoByRoute(routePath: string): { assetId: string; serviceName: string } | null {
+ // 精确匹配
+ if (ROUTE_ASSET_MAP[routePath]) {
+ return ROUTE_ASSET_MAP[routePath];
+ }
+
+ // 前缀匹配
+ for (const [prefix, info] of Object.entries(ROUTE_ASSET_MAP)) {
+ if (routePath.startsWith(prefix)) {
+ return info;
+ }
+ }
+
+ return null;
+}
+
+/**
+ * 显示服务开通弹窗
+ */
+export function showAssetSubscribeDialog(assetId: string, serviceName: string) {
+ console.log('[showAssetSubscribeDialog] 显示弹窗:', { assetId, serviceName });
+ console.log('[showAssetSubscribeDialog] 修改前状态:', JSON.stringify(assetSubscribeState.value));
+ assetSubscribeState.value.visible = true;
+ assetSubscribeState.value.assetId = assetId;
+ assetSubscribeState.value.serviceName = serviceName;
+ console.log('[showAssetSubscribeDialog] 修改后状态:', JSON.stringify(assetSubscribeState.value));
+}
+
+/**
+ * 关闭服务开通弹窗
+ */
+export function closeAssetSubscribeDialog() {
+ assetSubscribeState.value.visible = false;
+}
+
+/**
+ * 处理 403 错误码(模块未开通)
+ */
+export function handleModuleNotEnabled(routePath: string): boolean {
+ console.log('[模块未开通] 当前路由路径:', routePath);
+ const assetInfo = getAssetInfoByRoute(routePath);
+ console.log('[模块未开通] 匹配到的资产信息:', assetInfo);
+
+ if (assetInfo) {
+ showAssetSubscribeDialog(assetInfo.assetId, assetInfo.serviceName);
+ return true;
+ }
+
+ // 如果没有匹配到路由,尝试使用默认的资产管理
+ console.warn('[模块未开通] 未匹配到路由,使用默认资产管理');
+ showAssetSubscribeDialog('696b4acd1be1c8b76c4b4c15', '资产管理');
+ return true;
+}
diff --git a/src/utils/request.ts b/src/utils/request.ts
index cad63b3..5ca7d7a 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -3,6 +3,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { Session } from '/@/utils/storage';
import qs from 'qs';
import { getChangedFields } from '/@/utils/diffUtils';
+import { handleModuleNotEnabled } from '/@/utils/assetSubscribe';
// 标记是否正在处理 token 过期,避免重复弹窗
let isHandlingTokenExpired = false;
@@ -156,8 +157,20 @@ const responseInterceptor = (response: AxiosResponse) => {
return Promise.reject(new Error('登录状态已过期'));
}
- // 业务逻辑错误处理
- if (code !== undefined && code !== 0 && code !== 200) {
+ // 处理模块未开通错误 (403)
+ // 跳过资产SKU查询接口,避免弹窗内部请求触发循环
+ const requestUrl = response.config.url || '';
+ if (code === 402 && !requestUrl.includes('/assets/asset/sku/')) {
+ // 获取当前路由路径
+ const currentPath = window.location.hash.replace('#', '') || window.location.pathname;
+ console.log('[request.ts] 检测到403错误,当前路径:', currentPath);
+ handleModuleNotEnabled(currentPath);
+ // 直接返回,不再显示错误消息
+ return Promise.reject(new Error('模块未开通'));
+ }
+
+ // 业务逻辑错误处理(排除403,因为上面已处理)
+ if (code !== undefined && code !== 0 && code !== 200 && code !== 403) {
const errorMsg = message || `请求失败(${code})`;
showErrorMessage(errorMsg);
return Promise.reject(new Error(errorMsg));
@@ -189,10 +202,21 @@ const responseErrorHandler = (error: any) => {
const responseMessage = error.response.data?.message;
// 处理 HTTP 错误状态
+ const requestUrl = error.response.config?.url || '';
switch (httpStatus) {
case 401:
handleTokenExpired();
break;
+ case 402:
+ // 模块未开通处理,跳过SKU相关接口避免循环
+ if (!requestUrl.includes('/assets/asset/sku/') && !requestUrl.includes('getAssetAndSku')) {
+ const currentPath = window.location.hash.replace('#', '') || window.location.pathname;
+ console.log('[responseErrorHandler] 检测到HTTP 402错误,当前路径:', currentPath);
+ handleModuleNotEnabled(currentPath);
+ return Promise.reject(new Error('模块未开通'));
+ }
+ showErrorMessage(responseMessage || '服务未开通');
+ break;
case 403:
showErrorMessage(responseMessage || '没有权限访问该资源');
break;
From af17d81422477e04cafc0be830781ad464746f88 Mon Sep 17 00:00:00 2001
From: WUSIJIAN <13825895+wsj0228@user.noreply.gitee.com>
Date: Wed, 21 Jan 2026 14:02:42 +0800
Subject: [PATCH 27/31] =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=B5=84=E4=BA=A7?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E5=8A=9F=E8=83=BD,=E5=B0=86=E5=BC=B9?=
=?UTF-8?q?=E7=AA=97=E6=A8=A1=E5=BC=8F=E6=94=B9=E4=B8=BA=E5=A4=96=E9=83=A8?=
=?UTF-8?q?=E9=A1=B5=E9=9D=A2=E8=B7=B3=E8=BD=AC=E6=A8=A1=E5=BC=8F,?=
=?UTF-8?q?=E7=A7=BB=E9=99=A4AssetSubscribeDialog=E7=BB=84=E4=BB=B6?=
=?UTF-8?q?=E5=8F=8A=E5=85=B6=E5=9C=A8App.vue=E4=B8=AD=E7=9A=84=E5=BC=95?=
=?UTF-8?q?=E7=94=A8,=E4=BF=AE=E6=94=B9assetSubscribe=E5=B7=A5=E5=85=B7?=
=?UTF-8?q?=E7=B1=BB=E5=B0=86showAssetSubscribeDialog=E6=96=B9=E6=B3=95?=
=?UTF-8?q?=E6=9B=BF=E6=8D=A2=E4=B8=BAredirectToSubscribePage=E6=96=B9?=
=?UTF-8?q?=E6=B3=95,=E5=BD=93=E6=A3=80=E6=B5=8B=E5=88=B0402=E7=8A=B6?=
=?UTF-8?q?=E6=80=81=E7=A0=81=E6=97=B6=E7=9B=B4=E6=8E=A5=E8=B7=B3=E8=BD=AC?=
=?UTF-8?q?=E5=88=B0=E5=A4=96=E9=83=A8=E5=BC=80=E9=80=9A=E9=A1=B5=E9=9D=A2?=
=?UTF-8?q?=E5=B9=B6=E6=90=BA=E5=B8=A6=E8=B5=84=E4=BA=A7ID=E5=92=8C?=
=?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=9C=B0=E5=9D=80=E5=8F=82=E6=95=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.development | 1 +
public/web/subscribe.html | 580 ++++++++++++++++++++++++
src/App.vue | 11 +-
src/components/assetSubscribe/index.vue | 277 -----------
src/utils/assetSubscribe.ts | 46 +-
5 files changed, 596 insertions(+), 319 deletions(-)
create mode 100644 public/web/subscribe.html
delete mode 100644 src/components/assetSubscribe/index.vue
diff --git a/.env.development b/.env.development
index 5fb1b59..4add404 100644
--- a/.env.development
+++ b/.env.development
@@ -6,3 +6,4 @@ VITE_API_URL = 'http://192.168.3.200:8808/'
# VITE_API_URL = 'http://192.168.3.11:8808/'
+
diff --git a/public/web/subscribe.html b/public/web/subscribe.html
new file mode 100644
index 0000000..3037141
--- /dev/null
+++ b/public/web/subscribe.html
@@ -0,0 +1,580 @@
+
+
+
+
+
+ 服务开通 - 智能营销服务平台
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/App.vue b/src/App.vue
index 61073d9..c06763c 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -4,12 +4,6 @@
-
-
@@ -25,12 +19,10 @@ import setIntroduction from '/@/utils/setIconfont';
import LockScreen from '/@/layout/lockScreen/index.vue';
import Setings from '/@/layout/navBars/breadcrumb/setings.vue';
import CloseFull from '/@/layout/navBars/breadcrumb/closeFull.vue';
-import AssetSubscribeDialog from '/@/components/assetSubscribe/index.vue';
-import { assetSubscribeState } from '/@/utils/assetSubscribe';
export default defineComponent({
name: 'app',
- components: { LockScreen, Setings, CloseFull, AssetSubscribeDialog },
+ components: { LockScreen, Setings, CloseFull },
setup() {
const { proxy } = getCurrentInstance();
const setingsRef = ref();
@@ -97,7 +89,6 @@ export default defineComponent({
themeConfig,
setingsRef,
getGlobalComponentSize,
- assetSubscribeState,
...toRefs(state),
};
},
diff --git a/src/components/assetSubscribe/index.vue b/src/components/assetSubscribe/index.vue
deleted file mode 100644
index c4ce99d..0000000
--- a/src/components/assetSubscribe/index.vue
+++ /dev/null
@@ -1,277 +0,0 @@
-
-
-
-
-
- {{ serviceName }}
- 服务未开通,请选择套餐进行开通
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ sku.specsCount }}
- {{ sku.specsUnit?.value || '' }}
-
-
- ¥
- {{ (sku.price / 100).toFixed(2) }}
-
-
-
-
-
-
-
- 取消
-
- 立即开通
-
-
-
-
-
-
-
-
diff --git a/src/utils/assetSubscribe.ts b/src/utils/assetSubscribe.ts
index 1df0289..27fead5 100644
--- a/src/utils/assetSubscribe.ts
+++ b/src/utils/assetSubscribe.ts
@@ -1,4 +1,5 @@
-import { ref } from 'vue';
+// 开通页面地址(public/web/subscribe.html)
+const SUBSCRIBE_PAGE_URL = '/web/subscribe.html';
// 路由路径与 assetId 的映射关系
const ROUTE_ASSET_MAP: Record = {
@@ -10,21 +11,8 @@ const ROUTE_ASSET_MAP: Record
// 聚合电商业务(资产管理)
'/assets': { assetId: '696b4acd1be1c8b76c4b4c15', serviceName: '资产管理' },
-
- // 订单
- // '/order': { assetId: '696b4acd1be1c8b76c4b4c15', serviceName: '资产管理' },
-
- // AI数字人
- // '/digitalHuman': { assetId: '696f421205e496ba4ccbe662', serviceName: 'AI客服' },
};
-// 当前弹窗状态(响应式,供组件使用)
-export const assetSubscribeState = ref({
- visible: false,
- assetId: '',
- serviceName: '',
-});
-
/**
* 根据路由路径获取对应的 assetId 和服务名称
*/
@@ -45,26 +33,20 @@ export function getAssetInfoByRoute(routePath: string): { assetId: string; servi
}
/**
- * 显示服务开通弹窗
+ * 跳转到外部开通页面
+ * @param assetId 资产ID
*/
-export function showAssetSubscribeDialog(assetId: string, serviceName: string) {
- console.log('[showAssetSubscribeDialog] 显示弹窗:', { assetId, serviceName });
- console.log('[showAssetSubscribeDialog] 修改前状态:', JSON.stringify(assetSubscribeState.value));
- assetSubscribeState.value.visible = true;
- assetSubscribeState.value.assetId = assetId;
- assetSubscribeState.value.serviceName = serviceName;
- console.log('[showAssetSubscribeDialog] 修改后状态:', JSON.stringify(assetSubscribeState.value));
+export function redirectToSubscribePage(assetId: string) {
+ // 当前页面地址作为返回地址
+ const returnUrl = encodeURIComponent(window.location.href);
+ // 构建跳转URL
+ const url = `${SUBSCRIBE_PAGE_URL}?assetId=${assetId}&returnUrl=${returnUrl}`;
+ console.log('[redirectToSubscribePage] 跳转到开通页面:', url);
+ window.location.href = url;
}
/**
- * 关闭服务开通弹窗
- */
-export function closeAssetSubscribeDialog() {
- assetSubscribeState.value.visible = false;
-}
-
-/**
- * 处理 403 错误码(模块未开通)
+ * 处理 402 错误码(模块未开通)
*/
export function handleModuleNotEnabled(routePath: string): boolean {
console.log('[模块未开通] 当前路由路径:', routePath);
@@ -72,12 +54,12 @@ export function handleModuleNotEnabled(routePath: string): boolean {
console.log('[模块未开通] 匹配到的资产信息:', assetInfo);
if (assetInfo) {
- showAssetSubscribeDialog(assetInfo.assetId, assetInfo.serviceName);
+ redirectToSubscribePage(assetInfo.assetId);
return true;
}
// 如果没有匹配到路由,尝试使用默认的资产管理
console.warn('[模块未开通] 未匹配到路由,使用默认资产管理');
- showAssetSubscribeDialog('696b4acd1be1c8b76c4b4c15', '资产管理');
+ redirectToSubscribePage('696b4acd1be1c8b76c4b4c15');
return true;
}
From b9795c2cc43e0cd161a4c403aa4aee954ec4994a Mon Sep 17 00:00:00 2001
From: WUSIJIAN <13825895+wsj0228@user.noreply.gitee.com>
Date: Wed, 21 Jan 2026 16:27:54 +0800
Subject: [PATCH 28/31] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=B5=84=E4=BA=A7?=
=?UTF-8?q?=E8=AE=A2=E9=98=85=E9=A1=B5=E9=9D=A2,=E6=96=B0=E5=A2=9E?=
=?UTF-8?q?=E7=94=A8=E6=88=B7=E7=B1=BB=E5=9E=8B=E9=80=89=E6=8B=A9=E5=8A=9F?=
=?UTF-8?q?=E8=83=BD,=E6=94=AF=E6=8C=81=E5=9C=A8=E5=BC=80=E9=80=9A?=
=?UTF-8?q?=E6=9C=8D=E5=8A=A1=E6=97=B6=E9=80=89=E6=8B=A9=E7=A7=9F=E6=88=B7?=
=?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=B1=BB=E5=9E=8B,=E5=90=8C=E6=97=B6?=
=?UTF-8?q?=E4=BC=98=E5=8C=96=E5=BC=80=E9=80=9A=E6=88=90=E5=8A=9F=E5=90=8E?=
=?UTF-8?q?=E7=9A=84=E8=B7=B3=E8=BD=AC=E9=80=BB=E8=BE=91,=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A05=E7=A7=92=E5=86=85=E9=98=B2=E9=87=8D=E5=A4=8D?=
=?UTF-8?q?=E8=A7=A6=E5=8F=91402=E7=8A=B6=E6=80=81=E7=A0=81=E7=9A=84?=
=?UTF-8?q?=E4=BF=9D=E6=8A=A4=E6=9C=BA=E5=88=B6,=E9=81=BF=E5=85=8D?=
=?UTF-8?q?=E5=BC=80=E9=80=9A=E5=AE=8C=E6=88=90=E5=90=8E=E7=AB=8B=E5=8D=B3?=
=?UTF-8?q?=E8=B7=B3=E8=BD=AC=E5=8F=88=E8=A7=A6=E5=8F=91=E6=9C=AA=E5=BC=80?=
=?UTF-8?q?=E9=80=9A=E6=8F=90=E7=A4=BA=E7=9A=84=E5=BE=AA=E7=8E=AF=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/web/subscribe.html | 202 ++++++++++++++++++++++++++++++++++----
src/utils/request.ts | 9 ++
2 files changed, 190 insertions(+), 21 deletions(-)
diff --git a/public/web/subscribe.html b/public/web/subscribe.html
index 3037141..a584e41 100644
--- a/public/web/subscribe.html
+++ b/public/web/subscribe.html
@@ -86,6 +86,64 @@
margin: 0;
}
+ .section-title {
+ font-size: 0.875rem;
+ font-weight: 600;
+ color: #475569;
+ margin-bottom: 12px;
+ }
+
+ .type-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12px;
+ margin-bottom: 24px;
+ }
+
+ @media (max-width: 600px) {
+ .type-grid {
+ grid-template-columns: 1fr;
+ }
+ }
+
+ .type-card {
+ background: #fff;
+ border: 2px solid #e2e8f0;
+ border-radius: 10px;
+ padding: 14px 16px;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ text-align: center;
+ position: relative;
+ }
+
+ .type-card:hover {
+ border-color: #10b981;
+ background: #f0fdf4;
+ }
+
+ .type-card.selected {
+ border-color: #10b981;
+ background: #ecfdf5;
+ }
+
+ .type-card.selected::after {
+ content: '';
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ width: 18px;
+ height: 18px;
+ background: #10b981 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='white'%3E%3Cpath d='M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'/%3E%3C/svg%3E") center/12px no-repeat;
+ border-radius: 50%;
+ }
+
+ .type-card .type-name {
+ font-size: 0.95rem;
+ font-weight: 500;
+ color: #334155;
+ }
+
.sku-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
@@ -292,7 +350,7 @@
+
+